<commit_before>package initializer_test

import (
	"encoding/asn1"
	"net/http"
	"os"
	"time"

	"code.cloudfoundry.org/clock/fakeclock"
	"code.cloudfoundry.org/executor"
	"code.cloudfoundry.org/executor/depot/containerstore"
	"code.cloudfoundry.org/executor/initializer"
	"code.cloudfoundry.org/executor/initializer/configuration"
	"code.cloudfoundry.org/garden"
	"code.cloudfoundry.org/lager"
	"code.cloudfoundry.org/lager/lagertest"
	fake_metric "github.com/cloudfoundry/dropsonde/metric_sender/fake"
	"github.com/cloudfoundry/dropsonde/metrics"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/ghttp"
)

var _ = Describe("Initializer", func() {
	var initialTime time.Time
	var sender *fake_metric.FakeMetricSender
	var fakeGarden *ghttp.Server
	var fakeClock *fakeclock.FakeClock
	var errCh chan error
	var done chan struct{}
	var config initializer.ExecutorConfig
	var logger lager.Logger

	BeforeEach(func() {
		initialTime = time.Now()
		sender = fake_metric.NewFakeMetricSender()
		metrics.Initialize(sender, nil)
		fakeGarden = ghttp.NewUnstartedServer()
		fakeClock = fakeclock.NewFakeClock(initialTime)
		errCh = make(chan error, 1)
		done = make(chan struct{})
		logger = lagertest.NewTestLogger("test")

		fakeGarden.RouteToHandler("GET", "/ping", ghttp.RespondWithJSONEncoded(http.StatusOK, struct{}{}))
		fakeGarden.RouteToHandler("GET", "/containers", ghttp.RespondWithJSONEncoded(http.StatusOK, struct{}{}))
		fakeGarden.RouteToHandler("GET", "/capacity", ghttp.RespondWithJSONEncoded(http.StatusOK,
			garden.Capacity{MemoryInBytes: 1024 * 1024 * 1024, DiskInBytes: 2048 * 1024 * 1024, MaxContainers: 4}))
		fakeGarden.RouteToHandler("GET", "/containers/bulk_info", ghttp.RespondWithJSONEncoded(http.StatusOK, struct{}{}))
		config = initializer.ExecutorConfig{
			CachePath:                          "/tmp/cache",
			ContainerInodeLimit:                200000,
			ContainerMaxCpuShares:              0,
			ContainerMetricsReportInterval:     initializer.Duration(15 * time.Second),
			ContainerOwnerName:                 "executor",
			ContainerReapInterval:              initializer.Duration(time.Minute),
			CreateWorkPoolSize:                 32,
			DeleteWorkPoolSize:                 32,
			DiskMB:                             configuration.Automatic,
			ExportNetworkEnvVars:               false,
			GardenAddr:                         "/tmp/garden.sock",
			GardenHealthcheckCommandRetryPause: initializer.Duration(1 * time.Second),
			GardenHealthcheckEmissionInterval:  initializer.Duration(30 * time.Second),
			GardenHealthcheckInterval:          initializer.Duration(10 * time.Minute),
			GardenHealthcheckProcessArgs:       []string{},
			GardenHealthcheckProcessEnv:        []string{},
			GardenHealthcheckTimeout:           initializer.Duration(10 * time.Minute),
			GardenNetwork:                      "unix",
			HealthCheckContainerOwnerName:      "executor-health-check",
			HealthCheckWorkPoolSize:            64,
			HealthyMonitoringInterval:          initializer.Duration(30 * time.Second),
			MaxCacheSizeInBytes:                10 * 1024 * 1024 * 1024,
			MaxConcurrentDownloads:             5,
			MemoryMB:                           configuration.Automatic,
			MetricsWorkPoolSize:                8,
			ReadWorkPoolSize:                   64,
			ReservedExpirationTime:             initializer.Duration(time.Minute),
			SkipCertVerify:                     false,
			TempDir:                            "/tmp",
			UnhealthyMonitoringInterval:        initializer.Duration(500 * time.Millisecond),
			VolmanDriverPaths:                  "/tmpvolman1:/tmp/volman2",
		}
	})

	AfterEach(func() {
		Eventually(done).Should(BeClosed())
		fakeGarden.Close()
	})

	JustBeforeEach(func() {
		fakeGarden.Start()
		config.GardenAddr = fakeGarden.HTTPTestServer.Listener.Addr().String()
		config.GardenNetwork = "tcp"
		go func() {
			_, _, err := initializer.Initialize(logger, config, "fake-rootfs", fakeClock)
			errCh <- err
			close(done)
		}()
	})

	checkStalledMetric := func() float64 {
		return sender.GetValue("StalledGardenDuration").Value
	}

	Context("when garden doesn't respond", func() {
		var waitChan chan struct{}

		BeforeEach(func() {
			waitChan = make(chan struct{})
			fakeGarden.RouteToHandler("GET", "/ping", func(w http.ResponseWriter, req *http.Request) {
				<-waitChan
				ghttp.RespondWithJSONEncoded(http.StatusOK, struct{}{})(w, req)
			})
		})

		AfterEach(func() {
			close(waitChan)
		})

		It("emits metrics when garden doesn't respond", func() {
			Consistently(checkStalledMetric, 10*time.Millisecond).Should(BeEquivalentTo(0))
			fakeClock.WaitForWatcherAndIncrement(initializer.StalledMetricHeartbeatInterval)
			Eventually(checkStalledMetric).Should(BeNumerically("~", fakeClock.Since(initialTime)))
		})
	})

	Context("when garden responds", func() {
		It("emits 0", func() {
			Eventually(func() bool { return sender.HasValue("StalledGardenDuration") }).Should(BeTrue())
			Expect(checkStalledMetric()).To(BeEquivalentTo(0))
			Consistently(errCh).ShouldNot(Receive(HaveOccurred()))
		})
	})

	Context("when garden responds with an error", func() {
		var retried chan struct{}

		BeforeEach(func() {
			callCount := 0
			retried = make(chan struct{})
			fakeGarden.RouteToHandler("GET", "/ping", func(w http.ResponseWriter, req *http.Request) {
				callCount++
				if callCount == 1 {
					ghttp.RespondWith(http.StatusInternalServerError, "")(w, req)
				} else if callCount == 2 {
					ghttp.RespondWithJSONEncoded(http.StatusOK, struct{}{})(w, req)
					close(retried)
				}
			})
		})

		It("retries on a timer until it succeeds", func() {
			Consistently(retried).ShouldNot(BeClosed())
			fakeClock.Increment(initializer.PingGardenInterval)
			Eventually(retried).Should(BeClosed())
		})

		It("emits zero once it succeeds", func() {
			Consistently(func() bool { return sender.HasValue("StalledGardenDuration") }).Should(BeFalse())
			fakeClock.Increment(initializer.PingGardenInterval)
			Eventually(func() bool { return sender.HasValue("StalledGardenDuration") }).Should(BeTrue())
			Expect(checkStalledMetric()).To(BeEquivalentTo(0))
		})

		Context("when the error is unrecoverable", func() {
			BeforeEach(func() {
				fakeGarden.RouteToHandler(
					"GET",
					"/ping",
					ghttp.RespondWith(http.StatusGatewayTimeout, `{ "Type": "UnrecoverableError" , "Message": "Extra Special Error Message"}`),
				)
			})

			It("returns an error", func() {
				Eventually(errCh).Should(Receive(BeAssignableToTypeOf(garden.UnrecoverableError{})))
			})
		})
	})

	Context("when the post setup hook is invalid", func() {
		BeforeEach(func() {
			config.PostSetupHook = "unescaped quote\\"
		})

		It("fails fast", func() {
			Eventually(errCh).Should(Receive(MatchError("EOF found after escape character")))
		})
	})

	Describe("configuring trusted CA bundle", func() {
		Context("when valid", func() {
			BeforeEach(func() {
				config.PathToCACertsForDownloads = "fixtures/ca-certs"
			})

			It("uses it for the cached downloader", func() {
				// not really an easy way to check this at this layer -- inigo
				// let's just check that our validation passes
				Consistently(errCh).ShouldNot(Receive(HaveOccurred()))
			})

			Context("when the cert bundle has extra leading and trailing spaces", func() {
				BeforeEach(func() {
					config.PathToCACertsForDownloads = "fixtures/ca-certs-with-spaces"
				})

				It("does not error", func() {
					Consistently(errCh).ShouldNot(Receive(HaveOccurred()))
				})
			})

			Context("when the cert bundle is empty", func() {
				BeforeEach(func() {
					config.PathToCACertsForDownloads = "fixtures/ca-certs-empty"
				})

				It("does not error", func() {
					Consistently(errCh).ShouldNot(Receive(HaveOccurred()))
				})
			})
		})

		Context("when certs are invalid", func() {
			BeforeEach(func() {
				config.PathToCACertsForDownloads = "fixtures/ca-certs-invalid"
			})

			It("fails", func() {
				Eventually(errCh).Should(Receive(MatchError("unable to load CA certificate")))
			})
		})

		Context("when path is invalid", func() {
			BeforeEach(func() {
				config.PathToCACertsForDownloads = "sandwich"
			})

			It("fails", func() {
				Eventually(errCh).Should(Receive(MatchError("Unable to open CA cert bundle 'sandwich'")))
			})
		})
	})

	Describe("CredManagerFromConfig", func() {
		var credManager containerstore.CredManager
		var err error
		var container executor.Container
		var logger *lagertest.TestLogger

		JustBeforeEach(func() {
			logger = lagertest.NewTestLogger("executor")
			container = executor.Container{
				Guid: "1234",
			}
			credManager, err = initializer.CredManagerFromConfig(logger, config, fakeClock)
		})

		Describe("when instance identity creds directory is not set", func() {
			BeforeEach(func() {
				config.InstanceIdentityCredDir = ""
			})

			It("returns a noop credential manager", func() {
				bindMounts, _, err := credManager.CreateCredDir(logger, container)
				Expect(bindMounts).To(BeEmpty())
				Expect(err).NotTo(HaveOccurred())
			})
		})

		Describe("when the instance identity creds directory is set", func() {
			BeforeEach(func() {
				config.InstanceIdentityCredDir = "fixtures/instance-id/"
				config.InstanceIdentityCAPath = "fixtures/instance-id/ca.crt"
				config.InstanceIdentityPrivateKeyPath = "fixtures/instance-id/ca.key"
			})

			It("returns a credential manager", func() {
				bindMounts, _, err := credManager.CreateCredDir(logger, container)
				defer credManager.RemoveCreds(logger, container)
				Expect(err).NotTo(HaveOccurred())
				Expect(bindMounts).NotTo(BeEmpty())
			})

			Context("when the private key does not exist", func() {
				BeforeEach(func() {
					config.InstanceIdentityPrivateKeyPath = "fixtures/instance-id/notexist.key"
				})

				It("fails", func() {
					Eventually(os.IsNotExist(err)).Should(BeTrue())
				})
			})

			Context("when the private key is not PEM-encoded", func() {
				BeforeEach(func() {
					config.InstanceIdentityPrivateKeyPath = "fixtures/instance-id/non-pem.key"
				})

				It("fails", func() {
					Eventually(err).Should(MatchError(ContainSubstring("instance ID key is not PEM-encoded")))
				})
			})

			Context("when the private key is invalid", func() {
				BeforeEach(func() {
					config.InstanceIdentityPrivateKeyPath = "fixtures/instance-id/invalid.key"
				})

				It("fails", func() {
					Eventually(err).Should(BeAssignableToTypeOf(asn1.StructuralError{}))
				})
			})

			Context("when the certificate does not exist", func() {
				BeforeEach(func() {
					config.InstanceIdentityCAPath = "fixtures/instance-id/notexist.crt"
				})

				It("fails", func() {
					Eventually(os.IsNotExist(err)).Should(BeTrue())
				})
			})

			Context("when the certificate is not PEM-encoded", func() {
				BeforeEach(func() {
					config.InstanceIdentityCAPath = "fixtures/instance-id/non-pem.crt"
				})

				It("fails", func() {
					Eventually(err).Should(MatchError(ContainSubstring("instance ID CA is not PEM-encoded")))
				})
			})

			Context("when the certificate is invalid", func() {
				BeforeEach(func() {
					config.InstanceIdentityCAPath = "fixtures/instance-id/invalid.crt"
				})

				It("fails", func() {
					Eventually(err).Should(BeAssignableToTypeOf(asn1.StructuralError{}))
				})
			})
		})
	})
})
<commit_msg>adds description to 'file not found' errors assertion<commit_after>package initializer_test

import (
	"encoding/asn1"
	"net/http"
	"os"
	"time"

	"code.cloudfoundry.org/clock/fakeclock"
	"code.cloudfoundry.org/executor"
	"code.cloudfoundry.org/executor/depot/containerstore"
	"code.cloudfoundry.org/executor/initializer"
	"code.cloudfoundry.org/executor/initializer/configuration"
	"code.cloudfoundry.org/garden"
	"code.cloudfoundry.org/lager"
	"code.cloudfoundry.org/lager/lagertest"
	fake_metric "github.com/cloudfoundry/dropsonde/metric_sender/fake"
	"github.com/cloudfoundry/dropsonde/metrics"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/ghttp"
)

var _ = Describe("Initializer", func() {
	var initialTime time.Time
	var sender *fake_metric.FakeMetricSender
	var fakeGarden *ghttp.Server
	var fakeClock *fakeclock.FakeClock
	var errCh chan error
	var done chan struct{}
	var config initializer.ExecutorConfig
	var logger lager.Logger

	BeforeEach(func() {
		initialTime = time.Now()
		sender = fake_metric.NewFakeMetricSender()
		metrics.Initialize(sender, nil)
		fakeGarden = ghttp.NewUnstartedServer()
		fakeClock = fakeclock.NewFakeClock(initialTime)
		errCh = make(chan error, 1)
		done = make(chan struct{})
		logger = lagertest.NewTestLogger("test")

		fakeGarden.RouteToHandler("GET", "/ping", ghttp.RespondWithJSONEncoded(http.StatusOK, struct{}{}))
		fakeGarden.RouteToHandler("GET", "/containers", ghttp.RespondWithJSONEncoded(http.StatusOK, struct{}{}))
		fakeGarden.RouteToHandler("GET", "/capacity", ghttp.RespondWithJSONEncoded(http.StatusOK,
			garden.Capacity{MemoryInBytes: 1024 * 1024 * 1024, DiskInBytes: 2048 * 1024 * 1024, MaxContainers: 4}))
		fakeGarden.RouteToHandler("GET", "/containers/bulk_info", ghttp.RespondWithJSONEncoded(http.StatusOK, struct{}{}))
		config = initializer.ExecutorConfig{
			CachePath:                          "/tmp/cache",
			ContainerInodeLimit:                200000,
			ContainerMaxCpuShares:              0,
			ContainerMetricsReportInterval:     initializer.Duration(15 * time.Second),
			ContainerOwnerName:                 "executor",
			ContainerReapInterval:              initializer.Duration(time.Minute),
			CreateWorkPoolSize:                 32,
			DeleteWorkPoolSize:                 32,
			DiskMB:                             configuration.Automatic,
			ExportNetworkEnvVars:               false,
			GardenAddr:                         "/tmp/garden.sock",
			GardenHealthcheckCommandRetryPause: initializer.Duration(1 * time.Second),
			GardenHealthcheckEmissionInterval:  initializer.Duration(30 * time.Second),
			GardenHealthcheckInterval:          initializer.Duration(10 * time.Minute),
			GardenHealthcheckProcessArgs:       []string{},
			GardenHealthcheckProcessEnv:        []string{},
			GardenHealthcheckTimeout:           initializer.Duration(10 * time.Minute),
			GardenNetwork:                      "unix",
			HealthCheckContainerOwnerName:      "executor-health-check",
			HealthCheckWorkPoolSize:            64,
			HealthyMonitoringInterval:          initializer.Duration(30 * time.Second),
			MaxCacheSizeInBytes:                10 * 1024 * 1024 * 1024,
			MaxConcurrentDownloads:             5,
			MemoryMB:                           configuration.Automatic,
			MetricsWorkPoolSize:                8,
			ReadWorkPoolSize:                   64,
			ReservedExpirationTime:             initializer.Duration(time.Minute),
			SkipCertVerify:                     false,
			TempDir:                            "/tmp",
			UnhealthyMonitoringInterval:        initializer.Duration(500 * time.Millisecond),
			VolmanDriverPaths:                  "/tmpvolman1:/tmp/volman2",
		}
	})

	AfterEach(func() {
		Eventually(done).Should(BeClosed())
		fakeGarden.Close()
	})

	JustBeforeEach(func() {
		fakeGarden.Start()
		config.GardenAddr = fakeGarden.HTTPTestServer.Listener.Addr().String()
		config.GardenNetwork = "tcp"
		go func() {
			_, _, err := initializer.Initialize(logger, config, "fake-rootfs", fakeClock)
			errCh <- err
			close(done)
		}()
	})

	checkStalledMetric := func() float64 {
		return sender.GetValue("StalledGardenDuration").Value
	}

	Context("when garden doesn't respond", func() {
		var waitChan chan struct{}

		BeforeEach(func() {
			waitChan = make(chan struct{})
			fakeGarden.RouteToHandler("GET", "/ping", func(w http.ResponseWriter, req *http.Request) {
				<-waitChan
				ghttp.RespondWithJSONEncoded(http.StatusOK, struct{}{})(w, req)
			})
		})

		AfterEach(func() {
			close(waitChan)
		})

		It("emits metrics when garden doesn't respond", func() {
			Consistently(checkStalledMetric, 10*time.Millisecond).Should(BeEquivalentTo(0))
			fakeClock.WaitForWatcherAndIncrement(initializer.StalledMetricHeartbeatInterval)
			Eventually(checkStalledMetric).Should(BeNumerically("~", fakeClock.Since(initialTime)))
		})
	})

	Context("when garden responds", func() {
		It("emits 0", func() {
			Eventually(func() bool { return sender.HasValue("StalledGardenDuration") }).Should(BeTrue())
			Expect(checkStalledMetric()).To(BeEquivalentTo(0))
			Consistently(errCh).ShouldNot(Receive(HaveOccurred()))
		})
	})

	Context("when garden responds with an error", func() {
		var retried chan struct{}

		BeforeEach(func() {
			callCount := 0
			retried = make(chan struct{})
			fakeGarden.RouteToHandler("GET", "/ping", func(w http.ResponseWriter, req *http.Request) {
				callCount++
				if callCount == 1 {
					ghttp.RespondWith(http.StatusInternalServerError, "")(w, req)
				} else if callCount == 2 {
					ghttp.RespondWithJSONEncoded(http.StatusOK, struct{}{})(w, req)
					close(retried)
				}
			})
		})

		It("retries on a timer until it succeeds", func() {
			Consistently(retried).ShouldNot(BeClosed())
			fakeClock.Increment(initializer.PingGardenInterval)
			Eventually(retried).Should(BeClosed())
		})

		It("emits zero once it succeeds", func() {
			Consistently(func() bool { return sender.HasValue("StalledGardenDuration") }).Should(BeFalse())
			fakeClock.Increment(initializer.PingGardenInterval)
			Eventually(func() bool { return sender.HasValue("StalledGardenDuration") }).Should(BeTrue())
			Expect(checkStalledMetric()).To(BeEquivalentTo(0))
		})

		Context("when the error is unrecoverable", func() {
			BeforeEach(func() {
				fakeGarden.RouteToHandler(
					"GET",
					"/ping",
					ghttp.RespondWith(http.StatusGatewayTimeout, `{ "Type": "UnrecoverableError" , "Message": "Extra Special Error Message"}`),
				)
			})

			It("returns an error", func() {
				Eventually(errCh).Should(Receive(BeAssignableToTypeOf(garden.UnrecoverableError{})))
			})
		})
	})

	Context("when the post setup hook is invalid", func() {
		BeforeEach(func() {
			config.PostSetupHook = "unescaped quote\\"
		})

		It("fails fast", func() {
			Eventually(errCh).Should(Receive(MatchError("EOF found after escape character")))
		})
	})

	Describe("configuring trusted CA bundle", func() {
		Context("when valid", func() {
			BeforeEach(func() {
				config.PathToCACertsForDownloads = "fixtures/ca-certs"
			})

			It("uses it for the cached downloader", func() {
				// not really an easy way to check this at this layer -- inigo
				// let's just check that our validation passes
				Consistently(errCh).ShouldNot(Receive(HaveOccurred()))
			})

			Context("when the cert bundle has extra leading and trailing spaces", func() {
				BeforeEach(func() {
					config.PathToCACertsForDownloads = "fixtures/ca-certs-with-spaces"
				})

				It("does not error", func() {
					Consistently(errCh).ShouldNot(Receive(HaveOccurred()))
				})
			})

			Context("when the cert bundle is empty", func() {
				BeforeEach(func() {
					config.PathToCACertsForDownloads = "fixtures/ca-certs-empty"
				})

				It("does not error", func() {
					Consistently(errCh).ShouldNot(Receive(HaveOccurred()))
				})
			})
		})

		Context("when certs are invalid", func() {
			BeforeEach(func() {
				config.PathToCACertsForDownloads = "fixtures/ca-certs-invalid"
			})

			It("fails", func() {
				Eventually(errCh).Should(Receive(MatchError("unable to load CA certificate")))
			})
		})

		Context("when path is invalid", func() {
			BeforeEach(func() {
				config.PathToCACertsForDownloads = "sandwich"
			})

			It("fails", func() {
				Eventually(errCh).Should(Receive(MatchError("Unable to open CA cert bundle 'sandwich'")))
			})
		})
	})

	Describe("CredManagerFromConfig", func() {
		var credManager containerstore.CredManager
		var err error
		var container executor.Container
		var logger *lagertest.TestLogger

		JustBeforeEach(func() {
			logger = lagertest.NewTestLogger("executor")
			container = executor.Container{
				Guid: "1234",
			}
			credManager, err = initializer.CredManagerFromConfig(logger, config, fakeClock)
		})

		Describe("when instance identity creds directory is not set", func() {
			BeforeEach(func() {
				config.InstanceIdentityCredDir = ""
			})

			It("returns a noop credential manager", func() {
				bindMounts, _, err := credManager.CreateCredDir(logger, container)
				Expect(bindMounts).To(BeEmpty())
				Expect(err).NotTo(HaveOccurred())
			})
		})

		Describe("when the instance identity creds directory is set", func() {
			BeforeEach(func() {
				config.InstanceIdentityCredDir = "fixtures/instance-id/"
				config.InstanceIdentityCAPath = "fixtures/instance-id/ca.crt"
				config.InstanceIdentityPrivateKeyPath = "fixtures/instance-id/ca.key"
			})

			It("returns a credential manager", func() {
				bindMounts, _, err := credManager.CreateCredDir(logger, container)
				defer credManager.RemoveCreds(logger, container)
				Expect(err).NotTo(HaveOccurred())
				Expect(bindMounts).NotTo(BeEmpty())
			})

			Context("when the private key does not exist", func() {
				BeforeEach(func() {
					config.InstanceIdentityPrivateKeyPath = "fixtures/instance-id/notexist.key"
				})

				It("fails", func() {
					Eventually(os.IsNotExist(err)).Should(BeTrue(), "Private key does not exist")
				})
			})

			Context("when the private key is not PEM-encoded", func() {
				BeforeEach(func() {
					config.InstanceIdentityPrivateKeyPath = "fixtures/instance-id/non-pem.key"
				})

				It("fails", func() {
					Eventually(err).Should(MatchError(ContainSubstring("instance ID key is not PEM-encoded")))
				})
			})

			Context("when the private key is invalid", func() {
				BeforeEach(func() {
					config.InstanceIdentityPrivateKeyPath = "fixtures/instance-id/invalid.key"
				})

				It("fails", func() {
					Eventually(err).Should(BeAssignableToTypeOf(asn1.StructuralError{}))
				})
			})

			Context("when the certificate does not exist", func() {
				BeforeEach(func() {
					config.InstanceIdentityCAPath = "fixtures/instance-id/notexist.crt"
				})

				It("fails", func() {
					Eventually(os.IsNotExist(err)).Should(BeTrue(), "Instance certificate does not exist")
				})
			})

			Context("when the certificate is not PEM-encoded", func() {
				BeforeEach(func() {
					config.InstanceIdentityCAPath = "fixtures/instance-id/non-pem.crt"
				})

				It("fails", func() {
					Eventually(err).Should(MatchError(ContainSubstring("instance ID CA is not PEM-encoded")))
				})
			})

			Context("when the certificate is invalid", func() {
				BeforeEach(func() {
					config.InstanceIdentityCAPath = "fixtures/instance-id/invalid.crt"
				})

				It("fails", func() {
					Eventually(err).Should(BeAssignableToTypeOf(asn1.StructuralError{}))
				})
			})
		})
	})
})
<|endoftext|>


<commit_before>package looli
<commit_msg>feat: add test for Error<commit_after>package looli

import (
	"encoding/json"
	"errors"
	"github.com/stretchr/testify/assert"
	"net/http"
	"testing"
)

func TestNewError(t *testing.T) {
	data := JSON{
		"name": "cssivision",
	}

	err := &Error{
		Err:  errors.New("error happen"),
		Code: http.StatusOK,
		Meta: data,
	}

	assert.Equal(t, "error happen", err.Error())
	assert.Equal(t, http.StatusOK, err.Code)
	want, e := json.Marshal(data)
	assert.Nil(t, e)
	get, e := json.Marshal(err.Meta)
	assert.Nil(t, e)
	assert.Equal(t, want, get)
}
<|endoftext|>


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

package translate

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"reflect"
	"strings"
	"testing"

	"golang.org/x/net/context"
	"golang.org/x/text/language"
	"google.golang.org/api/option"
)

func initTest(ctx context.Context, t *testing.T) *Client {
	if testing.Short() {
		t.Skip("integration tests skipped in short mode")
	}
	apiKey := os.Getenv("GCLOUD_TESTS_API_KEY")
	if apiKey == "" {
		t.Skip("integration tests skipped: GCLOUD_TESTS_API_KEY not defined")
	}
	client, err := NewClient(ctx, option.WithAPIKey(apiKey))
	if err != nil {
		t.Fatalf("NewClient: %v", err)
	}
	return client
}

type fakeTransport struct {
	req *http.Request
}

func (t *fakeTransport) RoundTrip(req *http.Request) (*http.Response, error) {
	t.req = req
	return &http.Response{
		Status:     fmt.Sprintf("%d OK", http.StatusOK),
		StatusCode: http.StatusOK,
		Body:       ioutil.NopCloser(strings.NewReader("{}")),
	}, nil
}

func TestTranslateURL(t *testing.T) {
	// The translate API has all inputs in the URL.
	// Make sure we generate the right one.
	ctx := context.Background()
	ft := &fakeTransport{}
	c, err := NewClient(ctx, option.WithHTTPClient(&http.Client{Transport: ft}))
	if err != nil {
		t.Fatal(err)
	}
	for _, test := range []struct {
		target language.Tag
		inputs []string
		opts   *Options
		want   url.Values
	}{
		{language.Spanish, []string{"text"}, nil, url.Values{
			"q":      []string{"text"},
			"target": []string{"es"},
		}},
		{language.English, []string{"text"}, &Options{}, url.Values{
			"q":      []string{"text"},
			"target": []string{"en"},
		}},
		{language.Turkish, []string{"t1", "t2"}, nil, url.Values{
			"q":      []string{"t1", "t2"},
			"target": []string{"tr"},
		}},
		{language.English, []string{"text"}, &Options{Source: language.French},
			url.Values{
				"q":      []string{"text"},
				"source": []string{"fr"},
				"target": []string{"en"},
			},
		},
		{language.English, []string{"text"}, &Options{Source: language.French, Format: HTML}, url.Values{
			"q":      []string{"text"},
			"source": []string{"fr"},
			"format": []string{"html"},
			"target": []string{"en"},
		}},
	} {
		_, err = c.Translate(ctx, test.inputs, test.target, test.opts)
		if err != nil {
			t.Fatal(err)
		}
		got := ft.req.URL.Query()
		test.want.Add("alt", "json")
		if !reflect.DeepEqual(got, test.want) {
			t.Errorf("Translate(%s, %v, %+v):\ngot  %s\nwant %s",
				test.target, test.inputs, test.opts, got, test.want)
		}
	}
}

func TestTranslateOneInput(t *testing.T) {
	ctx := context.Background()
	c := initTest(ctx, t)
	defer c.Close()

	translate := func(input string, target language.Tag, opts *Options) Translation {
		ts, err := c.Translate(ctx, []string{input}, target, opts)
		if err != nil {
			t.Fatal(err)
		}
		if len(ts) != 1 {
			t.Fatalf("wanted one Translation, got %d", len(ts))
		}
		return ts[0]
	}

	for _, test := range []struct {
		input  string
		source language.Tag
		output string
		target language.Tag
	}{
		// https://www.youtube.com/watch?v=x1sQkEfAdfY
		{"Le singe est sur la branche", language.French,
			"The monkey is on the branch", language.English},
		// https://www.youtube.com/watch?v=akbflkF_1zY
		{"I will not buy this record, it is scratched", language.English,
			"Nem fogok vásárolni ezt a lemezt, azt karcos", language.Hungarian},
	} {
		// Provide source and format.
		tr := translate(test.input, test.target, &Options{Source: test.source, Format: Text})
		if got, want := tr.Source, language.Und; got != want {
			t.Errorf("source: got %q, wanted %q", got, want)
			continue
		}
		if got, want := tr.Text, test.output; got != want {
			t.Errorf("text: got %q, want %q", got, want)
		}
		// Omit source; it should be detected.
		tr = translate(test.input, test.target, &Options{Format: Text})
		if got, want := tr.Source, test.source; got != want {
			t.Errorf("source: got %q, wanted %q", got, want)
			continue
		}
		if got, want := tr.Text, test.output; got != want {
			t.Errorf("text: got %q, want %q", got, want)
		}

		// Omit format. Defaults to HTML. Still works with plain text.
		tr = translate(test.input, test.target, nil)
		if got, want := tr.Source, test.source; got != want {
			t.Errorf("source: got %q, wanted %q", got, want)
			continue
		}
		if got, want := tr.Text, test.output; got != want {
			t.Errorf("text: got %q, want %q", got, want)
		}

		// Add HTML tags to input. They should be in output.
		htmlify := func(s string) string {
			return "" + s + ""
		}
		tr = translate(htmlify(test.input), test.target, nil)
		if got, want := tr.Text, htmlify(test.output); got != want {
			t.Errorf("html: got %q, want %q", got, want)
		}
		// Using the HTML format behaves the same.
		tr = translate(htmlify(test.input), test.target, &Options{Format: HTML})
		if got, want := tr.Text, htmlify(test.output); got != want {
			t.Errorf("html: got %q, want %q", got, want)
		}
	}
}

// This tests the beta "nmt" model.
func TestTranslateModel(t *testing.T) {
	ctx := context.Background()
	c := initTest(ctx, t)
	defer c.Close()

	trs, err := c.Translate(ctx, []string{"Hello"}, language.French, &Options{Model: "nmt"})
	if err != nil {
		t.Fatal(err)
	}
	if len(trs) != 1 {
		t.Fatalf("wanted one Translation, got %d", len(trs))
	}
	tr := trs[0]
	if got, want := tr.Text, "Bonjour"; got != want {
		t.Errorf("text: got %q, want %q", got, want)
	}
	if got, want := tr.Model, "nmt"; got != want {
		t.Errorf("model: got %q, want %q", got, want)
	}
}

func TestTranslateMultipleInputs(t *testing.T) {
	ctx := context.Background()
	c := initTest(ctx, t)
	defer c.Close()

	inputs := []string{
		"When you're a Jet, you're a Jet all the way",
		"From your first cigarette to your last dying day",
		"When you're a Jet if the spit hits the fan",
		"You got brothers around, you're a family man",
	}
	ts, err := c.Translate(ctx, inputs, language.French, nil)
	if err != nil {
		t.Fatal(err)
	}
	if got, want := len(ts), len(inputs); got != want {
		t.Fatalf("got %d Translations, wanted %d", got, want)
	}
}

func TestTranslateErrors(t *testing.T) {
	ctx := context.Background()
	c := initTest(ctx, t)
	defer c.Close()

	for _, test := range []struct {
		ctx    context.Context
		target language.Tag
		inputs []string
		opts   *Options
	}{
		{ctx, language.English, nil, nil},
		{ctx, language.Und, []string{"input"}, nil},
		{ctx, language.English, []string{}, nil},
		{ctx, language.English, []string{"input"}, &Options{Format: "random"}},
	} {
		_, err := c.Translate(test.ctx, test.inputs, test.target, test.opts)
		if err == nil {
			t.Errorf("%+v: got nil, want error", test)
		}
	}
}

func TestDetectLanguage(t *testing.T) {
	ctx := context.Background()
	c := initTest(ctx, t)
	defer c.Close()
	ds, err := c.DetectLanguage(ctx, []string{
		"Today is Monday",
		"Aujourd'hui est lundi",
	})
	if err != nil {
		t.Fatal(err)
	}
	if len(ds) != 2 {
		t.Fatalf("got %d detection lists, want 2", len(ds))
	}
	checkDetections(t, ds[0], language.English)
	checkDetections(t, ds[1], language.French)
}

func checkDetections(t *testing.T, ds []Detection, want language.Tag) {
	for _, d := range ds {
		if d.Language == want {
			return
		}
	}
	t.Errorf("%v: missing %s", ds, want)
}

// A small subset of the supported languages.
var supportedLangs = []Language{
	{Name: "Danish", Tag: language.Danish},
	{Name: "English", Tag: language.English},
	{Name: "French", Tag: language.French},
	{Name: "German", Tag: language.German},
	{Name: "Greek", Tag: language.Greek},
	{Name: "Hindi", Tag: language.Hindi},
	{Name: "Hungarian", Tag: language.Hungarian},
	{Name: "Italian", Tag: language.Italian},
	{Name: "Russian", Tag: language.Russian},
	{Name: "Turkish", Tag: language.Turkish},
}

func TestSupportedLanguages(t *testing.T) {
	ctx := context.Background()
	c := initTest(ctx, t)
	defer c.Close()
	got, err := c.SupportedLanguages(ctx, language.English)
	if err != nil {
		t.Fatal(err)
	}
	want := map[language.Tag]Language{}
	for _, sl := range supportedLangs {
		want[sl.Tag] = sl
	}
	for _, g := range got {
		w, ok := want[g.Tag]
		if !ok {
			continue
		}
		if g != w {
			t.Errorf("got %+v, want %+v", g, w)
		}
		delete(want, g.Tag)
	}
	if len(want) > 0 {
		t.Errorf("missing: %+v", want)
	}
}
<commit_msg>translate: tests use OAuth or API key<commit_after>// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package translate

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"reflect"
	"strings"
	"sync"
	"testing"

	"cloud.google.com/go/internal/testutil"

	"golang.org/x/net/context"
	"golang.org/x/text/language"
	"google.golang.org/api/option"
)

var (
	once    sync.Once
	authOpt option.ClientOption
)

func initTest(ctx context.Context, t *testing.T) *Client {
	if testing.Short() {
		t.Skip("integration tests skipped in short mode")
	}
	once.Do(func() { authOpt = authOption() })
	if authOpt == nil {
		t.Skip("Integration tests skipped. See CONTRIBUTING.md for details")
	}
	client, err := NewClient(ctx, authOpt)
	if err != nil {
		t.Fatalf("NewClient: %v", err)
	}
	return client
}

func authOption() option.ClientOption {
	ts := testutil.TokenSource(context.Background(), Scope)
	if ts != nil {
		log.Println("authenticating via OAuth2")
		return option.WithTokenSource(ts)
	}
	apiKey := os.Getenv("GCLOUD_TESTS_API_KEY")
	if apiKey != "" {
		log.Println("authenticating with API key")
		return option.WithAPIKey(apiKey)
	}
	return nil
}

type fakeTransport struct {
	req *http.Request
}

func (t *fakeTransport) RoundTrip(req *http.Request) (*http.Response, error) {
	t.req = req
	return &http.Response{
		Status:     fmt.Sprintf("%d OK", http.StatusOK),
		StatusCode: http.StatusOK,
		Body:       ioutil.NopCloser(strings.NewReader("{}")),
	}, nil
}

func TestTranslateURL(t *testing.T) {
	// The translate API has all inputs in the URL.
	// Make sure we generate the right one.
	ctx := context.Background()
	ft := &fakeTransport{}
	c, err := NewClient(ctx, option.WithHTTPClient(&http.Client{Transport: ft}))
	if err != nil {
		t.Fatal(err)
	}
	for _, test := range []struct {
		target language.Tag
		inputs []string
		opts   *Options
		want   url.Values
	}{
		{language.Spanish, []string{"text"}, nil, url.Values{
			"q":      []string{"text"},
			"target": []string{"es"},
		}},
		{language.English, []string{"text"}, &Options{}, url.Values{
			"q":      []string{"text"},
			"target": []string{"en"},
		}},
		{language.Turkish, []string{"t1", "t2"}, nil, url.Values{
			"q":      []string{"t1", "t2"},
			"target": []string{"tr"},
		}},
		{language.English, []string{"text"}, &Options{Source: language.French},
			url.Values{
				"q":      []string{"text"},
				"source": []string{"fr"},
				"target": []string{"en"},
			},
		},
		{language.English, []string{"text"}, &Options{Source: language.French, Format: HTML}, url.Values{
			"q":      []string{"text"},
			"source": []string{"fr"},
			"format": []string{"html"},
			"target": []string{"en"},
		}},
	} {
		_, err = c.Translate(ctx, test.inputs, test.target, test.opts)
		if err != nil {
			t.Fatal(err)
		}
		got := ft.req.URL.Query()
		test.want.Add("alt", "json")
		if !reflect.DeepEqual(got, test.want) {
			t.Errorf("Translate(%s, %v, %+v):\ngot  %s\nwant %s",
				test.target, test.inputs, test.opts, got, test.want)
		}
	}
}

func TestTranslateOneInput(t *testing.T) {
	ctx := context.Background()
	c := initTest(ctx, t)
	defer c.Close()

	translate := func(input string, target language.Tag, opts *Options) Translation {
		ts, err := c.Translate(ctx, []string{input}, target, opts)
		if err != nil {
			t.Fatal(err)
		}
		if len(ts) != 1 {
			t.Fatalf("wanted one Translation, got %d", len(ts))
		}
		return ts[0]
	}

	for _, test := range []struct {
		input  string
		source language.Tag
		output string
		target language.Tag
	}{
		// https://www.youtube.com/watch?v=x1sQkEfAdfY
		{"Le singe est sur la branche", language.French,
			"The monkey is on the branch", language.English},
		// https://www.youtube.com/watch?v=akbflkF_1zY
		{"I will not buy this record, it is scratched", language.English,
			"Nem fogok vásárolni ezt a lemezt, azt karcos", language.Hungarian},
	} {
		// Provide source and format.
		tr := translate(test.input, test.target, &Options{Source: test.source, Format: Text})
		if got, want := tr.Source, language.Und; got != want {
			t.Errorf("source: got %q, wanted %q", got, want)
			continue
		}
		if got, want := tr.Text, test.output; got != want {
			t.Errorf("text: got %q, want %q", got, want)
		}
		// Omit source; it should be detected.
		tr = translate(test.input, test.target, &Options{Format: Text})
		if got, want := tr.Source, test.source; got != want {
			t.Errorf("source: got %q, wanted %q", got, want)
			continue
		}
		if got, want := tr.Text, test.output; got != want {
			t.Errorf("text: got %q, want %q", got, want)
		}

		// Omit format. Defaults to HTML. Still works with plain text.
		tr = translate(test.input, test.target, nil)
		if got, want := tr.Source, test.source; got != want {
			t.Errorf("source: got %q, wanted %q", got, want)
			continue
		}
		if got, want := tr.Text, test.output; got != want {
			t.Errorf("text: got %q, want %q", got, want)
		}

		// Add HTML tags to input. They should be in output.
		htmlify := func(s string) string {
			return "" + s + ""
		}
		tr = translate(htmlify(test.input), test.target, nil)
		if got, want := tr.Text, htmlify(test.output); got != want {
			t.Errorf("html: got %q, want %q", got, want)
		}
		// Using the HTML format behaves the same.
		tr = translate(htmlify(test.input), test.target, &Options{Format: HTML})
		if got, want := tr.Text, htmlify(test.output); got != want {
			t.Errorf("html: got %q, want %q", got, want)
		}
	}
}

// This tests the beta "nmt" model.
func TestTranslateModel(t *testing.T) {
	ctx := context.Background()
	c := initTest(ctx, t)
	defer c.Close()

	trs, err := c.Translate(ctx, []string{"Hello"}, language.French, &Options{Model: "nmt"})
	if err != nil {
		t.Fatal(err)
	}
	if len(trs) != 1 {
		t.Fatalf("wanted one Translation, got %d", len(trs))
	}
	tr := trs[0]
	if got, want := tr.Text, "Bonjour"; got != want {
		t.Errorf("text: got %q, want %q", got, want)
	}
	if got, want := tr.Model, "nmt"; got != want {
		t.Errorf("model: got %q, want %q", got, want)
	}
}

func TestTranslateMultipleInputs(t *testing.T) {
	ctx := context.Background()
	c := initTest(ctx, t)
	defer c.Close()

	inputs := []string{
		"When you're a Jet, you're a Jet all the way",
		"From your first cigarette to your last dying day",
		"When you're a Jet if the spit hits the fan",
		"You got brothers around, you're a family man",
	}
	ts, err := c.Translate(ctx, inputs, language.French, nil)
	if err != nil {
		t.Fatal(err)
	}
	if got, want := len(ts), len(inputs); got != want {
		t.Fatalf("got %d Translations, wanted %d", got, want)
	}
}

func TestTranslateErrors(t *testing.T) {
	ctx := context.Background()
	c := initTest(ctx, t)
	defer c.Close()

	for _, test := range []struct {
		ctx    context.Context
		target language.Tag
		inputs []string
		opts   *Options
	}{
		{ctx, language.English, nil, nil},
		{ctx, language.Und, []string{"input"}, nil},
		{ctx, language.English, []string{}, nil},
		{ctx, language.English, []string{"input"}, &Options{Format: "random"}},
	} {
		_, err := c.Translate(test.ctx, test.inputs, test.target, test.opts)
		if err == nil {
			t.Errorf("%+v: got nil, want error", test)
		}
	}
}

func TestDetectLanguage(t *testing.T) {
	ctx := context.Background()
	c := initTest(ctx, t)
	defer c.Close()
	ds, err := c.DetectLanguage(ctx, []string{
		"Today is Monday",
		"Aujourd'hui est lundi",
	})
	if err != nil {
		t.Fatal(err)
	}
	if len(ds) != 2 {
		t.Fatalf("got %d detection lists, want 2", len(ds))
	}
	checkDetections(t, ds[0], language.English)
	checkDetections(t, ds[1], language.French)
}

func checkDetections(t *testing.T, ds []Detection, want language.Tag) {
	for _, d := range ds {
		if d.Language == want {
			return
		}
	}
	t.Errorf("%v: missing %s", ds, want)
}

// A small subset of the supported languages.
var supportedLangs = []Language{
	{Name: "Danish", Tag: language.Danish},
	{Name: "English", Tag: language.English},
	{Name: "French", Tag: language.French},
	{Name: "German", Tag: language.German},
	{Name: "Greek", Tag: language.Greek},
	{Name: "Hindi", Tag: language.Hindi},
	{Name: "Hungarian", Tag: language.Hungarian},
	{Name: "Italian", Tag: language.Italian},
	{Name: "Russian", Tag: language.Russian},
	{Name: "Turkish", Tag: language.Turkish},
}

func TestSupportedLanguages(t *testing.T) {
	ctx := context.Background()
	c := initTest(ctx, t)
	defer c.Close()
	got, err := c.SupportedLanguages(ctx, language.English)
	if err != nil {
		t.Fatal(err)
	}
	want := map[language.Tag]Language{}
	for _, sl := range supportedLangs {
		want[sl.Tag] = sl
	}
	for _, g := range got {
		w, ok := want[g.Tag]
		if !ok {
			continue
		}
		if g != w {
			t.Errorf("got %+v, want %+v", g, w)
		}
		delete(want, g.Tag)
	}
	if len(want) > 0 {
		t.Errorf("missing: %+v", want)
	}
}
<|endoftext|>


<commit_before>package main_test

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

	"v.io/core/veyron/lib/expect"
	"v.io/core/veyron/lib/modules"
	"v.io/core/veyron/lib/testutil/v23tests"
)

//go:generate v23 test generate
func TestHelperProcess(t *testing.T) {
	modules.DispatchInTest()
}

// redirect redirects the stdout of the given invocation to the file at the
// given path.
func redirect(t *v23tests.T, inv *v23tests.Invocation, path string) {
	if err := ioutil.WriteFile(path, []byte(inv.Output()), 0600); err != nil {
		t.Fatalf("WriteFile(%q) failed: %v\n", path, err)
	}
}

// removePublicKeys replaces public keys (16 hex bytes, :-separated) with
// XX:....  This substitution enables comparison with golden output even when
// keys are freshly minted by the "principal create" command.
func removePublicKeys(input string) string {
	return regexp.MustCompile("([0-9a-f]{2}:){15}[0-9a-f]{2}").ReplaceAllString(input, "XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX")
}

func removeCaveats(input string) string {
	return regexp.MustCompile("0x54a676398137187ecdb26d2d69ba0004\\(int64=.*\\)").ReplaceAllString(input, "ExpiryCaveat")
}

func V23TestBlessSelf(t *v23tests.T) {
	var (
		outputDir         = t.TempDir()
		aliceDir          = filepath.Join(outputDir, "alice")
		aliceBlessingFile = filepath.Join(outputDir, "aliceself")
	)

	bin := t.BuildGoPkg("v.io/core/veyron/tools/principal")
	bin.Start("create", aliceDir, "alice").WaitOrDie(os.Stdout, os.Stderr)

	bin = bin.WithEnv("VEYRON_CREDENTIALS=" + aliceDir)
	redirect(t, bin.Start("blessself", "alicereborn"), aliceBlessingFile)
	got := removePublicKeys(bin.Start("dumpblessings", aliceBlessingFile).Output())
	want := `Blessings          : alicereborn
PublicKey          : XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
Certificate chains : 1
Chain #0 (1 certificates). Root certificate public key: XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
  Certificate #0: alicereborn with 0 caveats
`
	if want != got {
		t.Fatalf("unexpected output, wanted \n%s, got\n%s", want, got)
	}
}

func V23TestStore(t *v23tests.T) {
	var (
		outputDir   = t.TempDir()
		bin         = t.BuildGoPkg("v.io/core/veyron/tools/principal")
		aliceDir    = filepath.Join(outputDir, "alice")
		aliceFriend = filepath.Join(outputDir, "alice.bless")
		bobDir      = filepath.Join(outputDir, "bob")
		bobForPeer  = filepath.Join(outputDir, "bob.store.forpeer")
	)

	// Create two principals: alice and bob.
	bin.Start("create", aliceDir, "alice").WaitOrDie(os.Stdout, os.Stderr)
	bin.Start("create", bobDir, "bob").WaitOrDie(os.Stdout, os.Stderr)

	// Bless Alice with Bob's principal.
	blessEnv := []string{"VEYRON_CREDENTIALS=" + aliceDir}
	redirect(t, bin.WithEnv(blessEnv...).Start("bless", bobDir, "friend"), aliceFriend)

	// Run store forpeer on bob.
	bin.Start("--veyron.credentials="+bobDir, "store", "set", aliceFriend, "alice").WaitOrDie(os.Stdout, os.Stderr)
	redirect(t, bin.WithEnv(blessEnv...).Start("--veyron.credentials="+bobDir, "store", "forpeer", "alice/server"), bobForPeer)

	got := removeCaveats(removePublicKeys(bin.Start("dumpblessings", bobForPeer).Output()))
	want := `Blessings          : bob#alice/friend
PublicKey          : XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
Certificate chains : 2
Chain #0 (1 certificates). Root certificate public key: XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
  Certificate #0: bob with 0 caveats
Chain #1 (2 certificates). Root certificate public key: XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
  Certificate #0: alice with 0 caveats
  Certificate #1: friend with 1 caveat
    (0) ExpiryCaveat
`
	if want != got {
		t.Fatalf("unexpected output, got\n%s, wanted\n%s", got, want)
	}
}

func V23TestDump(t *v23tests.T) {
	var (
		outputDir = t.TempDir()
		bin       = t.BuildGoPkg("v.io/core/veyron/tools/principal")
		aliceDir  = filepath.Join(outputDir, "alice")
	)

	bin.Start("create", aliceDir, "alice").WaitOrDie(os.Stdout, os.Stderr)

	blessEnv := []string{"VEYRON_CREDENTIALS=" + aliceDir}
	got := removePublicKeys(bin.WithEnv(blessEnv...).Start("dump").Output())
	want := `Public key : XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
---------------- BlessingStore ----------------
Default blessings: alice
Peer pattern                   : Blessings
...                            : alice
---------------- BlessingRoots ----------------
Public key                                      : Pattern
XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX : [alice]
`
	if want != got {
		t.Fatalf("unexpected output, got\n%s, wanted\n%s", got, want)
	}
}

func V23TestRecvBlessings(t *v23tests.T) {
	var (
		outputDir = t.TempDir()
		bin       = t.BuildGoPkg("v.io/core/veyron/tools/principal")
		aliceDir  = filepath.Join(outputDir, "alice")
		carolDir  = filepath.Join(outputDir, "carol")
	)

	// Generate principals for alice and carol.
	bin.Start("create", aliceDir, "alice").WaitOrDie(os.Stdout, os.Stderr)
	bin.Start("create", carolDir, "carol").WaitOrDie(os.Stdout, os.Stderr)

	// Run recvblessings on carol, and have alice send blessings over
	// (blessings received must be set as default and shareable with all peers).
	var args []string
	{
		inv := bin.Start("--veyron.credentials="+carolDir, "--veyron.tcp.address=127.0.0.1:0", "recvblessings")
		defer inv.Kill(syscall.SIGTERM)
		// recvblessings suggests a random extension, find the extension and replace it with friend/carol.
		e := expect.NewSession(t, inv.Stdout(), 3*time.Second)
		cmd := e.ExpectSetEventuallyRE("(^principal bless .*$)")[0][0]
		args = strings.Split(regexp.MustCompile("extension[0-9]*").ReplaceAllString(cmd, "friend/carol"), " ")
	}

	bin.WithEnv("VEYRON_CREDENTIALS="+aliceDir).Start(args[1:]...).WaitOrDie(os.Stdout, os.Stderr)

	// Run recvblessings on carol, and have alice send blessings over
	// (blessings received must be set as shareable with peers matching 'alice/...'.)
	{
		inv := bin.Start("--veyron.credentials="+carolDir, "--veyron.tcp.address=127.0.0.1:0", "recvblessings", "--for_peer=alice", "--set_default=false")
		defer inv.Kill(syscall.SIGTERM)
		// recvblessings suggests a random extension, find the extension and replace it with friend/carol/foralice.
		e := expect.NewSession(t, inv.Stdout(), 3*time.Second)
		cmd := e.ExpectSetEventuallyRE("(^principal bless .*$)")[0][0]
		args = strings.Split(regexp.MustCompile("extension[0-9]*").ReplaceAllString(cmd, "friend/carol/foralice"), " ")
	}
	bin.WithEnv("VEYRON_CREDENTIALS="+aliceDir).Start(args[1:]...).WaitOrDie(os.Stdout, os.Stderr)

	listenerInv := bin.Start("--veyron.credentials="+carolDir, "--veyron.tcp.address=127.0.0.1:0", "recvblessings", "--for_peer=alice/...", "--set_default=false", "--vmodule=*=2", "--logtostderr")
	defer listenerInv.Kill(syscall.SIGTERM)

	e := expect.NewSession(t, listenerInv.Stdout(), 3*time.Second)
	sendCmd := e.ExpectSetEventuallyRE("(^principal bless .*$)")[0][0]
	// Mucking around with the key should fail.
	args = strings.Split(regexp.MustCompile("remote_key=").ReplaceAllString(sendCmd, "remote_key=BAD"), " ")

	{
		var buf bytes.Buffer
		if bin.WithEnv("VEYRON_CREDENTIALS="+aliceDir).Start(args[1:]...).Wait(os.Stdout, &buf) == nil {
			t.Fatalf("sender should have failed but did not")
		}

		if want, got := "key mismatch", buf.String(); !strings.Contains(got, want) {
			t.Fatalf("expected %q to be contained within\n%s\n, but was not", want, got)
		}
	}

	{
		var buf bytes.Buffer
		// Mucking around with the token should fail.
		args = strings.Split(regexp.MustCompile("remote_token=").ReplaceAllString(sendCmd, "remote_token=BAD"), " ")
		if bin.WithEnv("VEYRON_CREDENTIALS="+aliceDir).Start(args[1:]...).Wait(os.Stdout, &buf) == nil {
			t.Fatalf("sender should have failed but did not")
		}

		if want, got := "blessings received from unexpected sender", buf.String(); !strings.Contains(got, want) {
			t.Fatalf("expected %q to be contained within\n%s\n, but was not", want, got)
		}
	}

	// Dump carol out, the only blessing that survives should be from the
	// first "bless" command. (alice/friend/carol).
	got := removePublicKeys(bin.Start("--veyron.credentials="+carolDir, "dump").Output())
	want := `Public key : XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
---------------- BlessingStore ----------------
Default blessings: alice/friend/carol
Peer pattern                   : Blessings
...                            : alice/friend/carol
alice                          : alice/friend/carol/foralice
---------------- BlessingRoots ----------------
Public key                                      : Pattern
XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX : [alice]
XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX : [carol]
`
	if want != got {
		t.Fatalf("unexpected output, got\n%s, wanted\n%s", got, want)
	}
}

func V23TestFork(t *v23tests.T) {
	var (
		outputDir             = t.TempDir()
		bin                   = t.BuildGoPkg("v.io/core/veyron/tools/principal")
		aliceDir              = filepath.Join(outputDir, "alice")
		alicePhoneDir         = filepath.Join(outputDir, "alice-phone")
		alicePhoneCalendarDir = filepath.Join(outputDir, "alice-phone-calendar")
	)

	// Generate principals for alice.
	bin.Start("create", aliceDir, "alice").WaitOrDie(os.Stdout, os.Stderr)

	// Run fork to setup up credentials for alice/phone that are
	// blessed by alice under the extension "phone".
	bin.Start("--veyron.credentials="+aliceDir, "fork", alicePhoneDir, "phone").WaitOrDie(os.Stdout, os.Stderr)

	// Dump alice-phone out, the only blessings it has must be from alice (alice/phone).
	{
		got := removePublicKeys(bin.Start("--veyron.credentials="+alicePhoneDir, "dump").Output())
		want := `Public key : XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
---------------- BlessingStore ----------------
Default blessings: alice/phone
Peer pattern                   : Blessings
...                            : alice/phone
---------------- BlessingRoots ----------------
Public key                                      : Pattern
XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX : [alice]
`
		if want != got {
			t.Fatalf("unexpected output, got\n%s, wanted\n%s", got, want)
		}
	}

	// Run fork to setup up credentials for alice/phone/calendar that are
	// blessed by alice/phone under the extension "calendar".
	bin.Start("--veyron.credentials="+alicePhoneDir, "fork", alicePhoneCalendarDir, "calendar").WaitOrDie(os.Stdout, os.Stderr)
	{
		got := removePublicKeys(bin.Start("--veyron.credentials="+alicePhoneCalendarDir, "dump").Output())
		want := `Public key : XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
---------------- BlessingStore ----------------
Default blessings: alice/phone/calendar
Peer pattern                   : Blessings
...                            : alice/phone/calendar
---------------- BlessingRoots ----------------
Public key                                      : Pattern
XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX : [alice]
`
		if want != got {
			t.Fatalf("unexpected output, got\n%s, wanted\n%s", got, want)
		}
	}
}

func V23TestCreate(t *v23tests.T) {
	var (
		outputDir = t.TempDir()
		bin       = t.BuildGoPkg("v.io/core/veyron/tools/principal")
		aliceDir  = filepath.Join(outputDir, "alice")
	)

	// Creating a principal should succeed the first time.
	bin.Start("create", aliceDir, "alice").WaitOrDie(os.Stdout, os.Stderr)

	// The second time should fail (the create command won't override an existing principal).
	if bin.Start("create", aliceDir, "alice").Wait(os.Stdout, os.Stderr) == nil {
		t.Fatalf("principal creation should have failed, but did not")
	}

	// If we specify -overwrite, it will.
	bin.Start("create", "--overwrite", aliceDir, "alice").WaitOrDie(os.Stdout, os.Stderr)
}
<commit_msg>veyron/tools/principal: use Invocation's expect facility<commit_after>package main_test

import (
	"bytes"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"syscall"
	"testing"

	"v.io/core/veyron/lib/modules"
	"v.io/core/veyron/lib/testutil/v23tests"
)

//go:generate v23 test generate
func TestHelperProcess(t *testing.T) {
	modules.DispatchInTest()
}

// redirect redirects the stdout of the given invocation to the file at the
// given path.
func redirect(t *v23tests.T, inv *v23tests.Invocation, path string) {
	if err := ioutil.WriteFile(path, []byte(inv.Output()), 0600); err != nil {
		t.Fatalf("WriteFile(%q) failed: %v\n", path, err)
	}
}

// removePublicKeys replaces public keys (16 hex bytes, :-separated) with
// XX:....  This substitution enables comparison with golden output even when
// keys are freshly minted by the "principal create" command.
func removePublicKeys(input string) string {
	return regexp.MustCompile("([0-9a-f]{2}:){15}[0-9a-f]{2}").ReplaceAllString(input, "XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX")
}

func removeCaveats(input string) string {
	return regexp.MustCompile("0x54a676398137187ecdb26d2d69ba0004\\(int64=.*\\)").ReplaceAllString(input, "ExpiryCaveat")
}

func V23TestBlessSelf(t *v23tests.T) {
	var (
		outputDir         = t.TempDir()
		aliceDir          = filepath.Join(outputDir, "alice")
		aliceBlessingFile = filepath.Join(outputDir, "aliceself")
	)

	bin := t.BuildGoPkg("v.io/core/veyron/tools/principal")
	bin.Start("create", aliceDir, "alice").WaitOrDie(os.Stdout, os.Stderr)

	bin = bin.WithEnv("VEYRON_CREDENTIALS=" + aliceDir)
	redirect(t, bin.Start("blessself", "alicereborn"), aliceBlessingFile)
	got := removePublicKeys(bin.Start("dumpblessings", aliceBlessingFile).Output())
	want := `Blessings          : alicereborn
PublicKey          : XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
Certificate chains : 1
Chain #0 (1 certificates). Root certificate public key: XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
  Certificate #0: alicereborn with 0 caveats
`
	if want != got {
		t.Fatalf("unexpected output, wanted \n%s, got\n%s", want, got)
	}
}

func V23TestStore(t *v23tests.T) {
	var (
		outputDir   = t.TempDir()
		bin         = t.BuildGoPkg("v.io/core/veyron/tools/principal")
		aliceDir    = filepath.Join(outputDir, "alice")
		aliceFriend = filepath.Join(outputDir, "alice.bless")
		bobDir      = filepath.Join(outputDir, "bob")
		bobForPeer  = filepath.Join(outputDir, "bob.store.forpeer")
	)

	// Create two principals: alice and bob.
	bin.Start("create", aliceDir, "alice").WaitOrDie(os.Stdout, os.Stderr)
	bin.Start("create", bobDir, "bob").WaitOrDie(os.Stdout, os.Stderr)

	// Bless Alice with Bob's principal.
	blessEnv := []string{"VEYRON_CREDENTIALS=" + aliceDir}
	redirect(t, bin.WithEnv(blessEnv...).Start("bless", bobDir, "friend"), aliceFriend)

	// Run store forpeer on bob.
	bin.Start("--veyron.credentials="+bobDir, "store", "set", aliceFriend, "alice").WaitOrDie(os.Stdout, os.Stderr)
	redirect(t, bin.WithEnv(blessEnv...).Start("--veyron.credentials="+bobDir, "store", "forpeer", "alice/server"), bobForPeer)

	got := removeCaveats(removePublicKeys(bin.Start("dumpblessings", bobForPeer).Output()))
	want := `Blessings          : bob#alice/friend
PublicKey          : XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
Certificate chains : 2
Chain #0 (1 certificates). Root certificate public key: XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
  Certificate #0: bob with 0 caveats
Chain #1 (2 certificates). Root certificate public key: XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
  Certificate #0: alice with 0 caveats
  Certificate #1: friend with 1 caveat
    (0) ExpiryCaveat
`
	if want != got {
		t.Fatalf("unexpected output, got\n%s, wanted\n%s", got, want)
	}
}

func V23TestDump(t *v23tests.T) {
	var (
		outputDir = t.TempDir()
		bin       = t.BuildGoPkg("v.io/core/veyron/tools/principal")
		aliceDir  = filepath.Join(outputDir, "alice")
	)

	bin.Start("create", aliceDir, "alice").WaitOrDie(os.Stdout, os.Stderr)

	blessEnv := []string{"VEYRON_CREDENTIALS=" + aliceDir}
	got := removePublicKeys(bin.WithEnv(blessEnv...).Start("dump").Output())
	want := `Public key : XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
---------------- BlessingStore ----------------
Default blessings: alice
Peer pattern                   : Blessings
...                            : alice
---------------- BlessingRoots ----------------
Public key                                      : Pattern
XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX : [alice]
`
	if want != got {
		t.Fatalf("unexpected output, got\n%s, wanted\n%s", got, want)
	}
}

func V23TestRecvBlessings(t *v23tests.T) {
	var (
		outputDir = t.TempDir()
		bin       = t.BuildGoPkg("v.io/core/veyron/tools/principal")
		aliceDir  = filepath.Join(outputDir, "alice")
		carolDir  = filepath.Join(outputDir, "carol")
	)

	// Generate principals for alice and carol.
	bin.Start("create", aliceDir, "alice").WaitOrDie(os.Stdout, os.Stderr)
	bin.Start("create", carolDir, "carol").WaitOrDie(os.Stdout, os.Stderr)

	// Run recvblessings on carol, and have alice send blessings over
	// (blessings received must be set as default and shareable with all peers).
	var args []string
	{
		inv := bin.Start("--veyron.credentials="+carolDir, "--veyron.tcp.address=127.0.0.1:0", "recvblessings")
		defer inv.Kill(syscall.SIGTERM)
		// recvblessings suggests a random extension, find the extension and replace it with friend/carol.
		cmd := inv.ExpectSetEventuallyRE("(^principal bless .*$)")[0][0]
		args = strings.Split(regexp.MustCompile("extension[0-9]*").ReplaceAllString(cmd, "friend/carol"), " ")
	}

	bin.WithEnv("VEYRON_CREDENTIALS="+aliceDir).Start(args[1:]...).WaitOrDie(os.Stdout, os.Stderr)

	// Run recvblessings on carol, and have alice send blessings over
	// (blessings received must be set as shareable with peers matching 'alice/...'.)
	{
		inv := bin.Start("--veyron.credentials="+carolDir, "--veyron.tcp.address=127.0.0.1:0", "recvblessings", "--for_peer=alice", "--set_default=false")
		defer inv.Kill(syscall.SIGTERM)
		// recvblessings suggests a random extension, find the extension and replace it with friend/carol/foralice.
		cmd := inv.ExpectSetEventuallyRE("(^principal bless .*$)")[0][0]
		args = strings.Split(regexp.MustCompile("extension[0-9]*").ReplaceAllString(cmd, "friend/carol/foralice"), " ")
	}
	bin.WithEnv("VEYRON_CREDENTIALS="+aliceDir).Start(args[1:]...).WaitOrDie(os.Stdout, os.Stderr)

	listenerInv := bin.Start("--veyron.credentials="+carolDir, "--veyron.tcp.address=127.0.0.1:0", "recvblessings", "--for_peer=alice/...", "--set_default=false", "--vmodule=*=2", "--logtostderr")
	defer listenerInv.Kill(syscall.SIGTERM)

	sendCmd := listenerInv.ExpectSetEventuallyRE("(^principal bless .*$)")[0][0]
	// Mucking around with the key should fail.
	args = strings.Split(regexp.MustCompile("remote_key=").ReplaceAllString(sendCmd, "remote_key=BAD"), " ")

	{
		var buf bytes.Buffer
		if bin.WithEnv("VEYRON_CREDENTIALS="+aliceDir).Start(args[1:]...).Wait(os.Stdout, &buf) == nil {
			t.Fatalf("sender should have failed but did not")
		}

		if want, got := "key mismatch", buf.String(); !strings.Contains(got, want) {
			t.Fatalf("expected %q to be contained within\n%s\n, but was not", want, got)
		}
	}

	{
		var buf bytes.Buffer
		// Mucking around with the token should fail.
		args = strings.Split(regexp.MustCompile("remote_token=").ReplaceAllString(sendCmd, "remote_token=BAD"), " ")
		if bin.WithEnv("VEYRON_CREDENTIALS="+aliceDir).Start(args[1:]...).Wait(os.Stdout, &buf) == nil {
			t.Fatalf("sender should have failed but did not")
		}

		if want, got := "blessings received from unexpected sender", buf.String(); !strings.Contains(got, want) {
			t.Fatalf("expected %q to be contained within\n%s\n, but was not", want, got)
		}
	}

	// Dump carol out, the only blessing that survives should be from the
	// first "bless" command. (alice/friend/carol).
	got := removePublicKeys(bin.Start("--veyron.credentials="+carolDir, "dump").Output())
	want := `Public key : XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
---------------- BlessingStore ----------------
Default blessings: alice/friend/carol
Peer pattern                   : Blessings
...                            : alice/friend/carol
alice                          : alice/friend/carol/foralice
---------------- BlessingRoots ----------------
Public key                                      : Pattern
XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX : [alice]
XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX : [carol]
`
	if want != got {
		t.Fatalf("unexpected output, got\n%s, wanted\n%s", got, want)
	}
}

func V23TestFork(t *v23tests.T) {
	var (
		outputDir             = t.TempDir()
		bin                   = t.BuildGoPkg("v.io/core/veyron/tools/principal")
		aliceDir              = filepath.Join(outputDir, "alice")
		alicePhoneDir         = filepath.Join(outputDir, "alice-phone")
		alicePhoneCalendarDir = filepath.Join(outputDir, "alice-phone-calendar")
	)

	// Generate principals for alice.
	bin.Start("create", aliceDir, "alice").WaitOrDie(os.Stdout, os.Stderr)

	// Run fork to setup up credentials for alice/phone that are
	// blessed by alice under the extension "phone".
	bin.Start("--veyron.credentials="+aliceDir, "fork", alicePhoneDir, "phone").WaitOrDie(os.Stdout, os.Stderr)

	// Dump alice-phone out, the only blessings it has must be from alice (alice/phone).
	{
		got := removePublicKeys(bin.Start("--veyron.credentials="+alicePhoneDir, "dump").Output())
		want := `Public key : XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
---------------- BlessingStore ----------------
Default blessings: alice/phone
Peer pattern                   : Blessings
...                            : alice/phone
---------------- BlessingRoots ----------------
Public key                                      : Pattern
XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX : [alice]
`
		if want != got {
			t.Fatalf("unexpected output, got\n%s, wanted\n%s", got, want)
		}
	}

	// Run fork to setup up credentials for alice/phone/calendar that are
	// blessed by alice/phone under the extension "calendar".
	bin.Start("--veyron.credentials="+alicePhoneDir, "fork", alicePhoneCalendarDir, "calendar").WaitOrDie(os.Stdout, os.Stderr)
	{
		got := removePublicKeys(bin.Start("--veyron.credentials="+alicePhoneCalendarDir, "dump").Output())
		want := `Public key : XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
---------------- BlessingStore ----------------
Default blessings: alice/phone/calendar
Peer pattern                   : Blessings
...                            : alice/phone/calendar
---------------- BlessingRoots ----------------
Public key                                      : Pattern
XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX : [alice]
`
		if want != got {
			t.Fatalf("unexpected output, got\n%s, wanted\n%s", got, want)
		}
	}
}

func V23TestCreate(t *v23tests.T) {
	var (
		outputDir = t.TempDir()
		bin       = t.BuildGoPkg("v.io/core/veyron/tools/principal")
		aliceDir  = filepath.Join(outputDir, "alice")
	)

	// Creating a principal should succeed the first time.
	bin.Start("create", aliceDir, "alice").WaitOrDie(os.Stdout, os.Stderr)

	// The second time should fail (the create command won't override an existing principal).
	if bin.Start("create", aliceDir, "alice").Wait(os.Stdout, os.Stderr) == nil {
		t.Fatalf("principal creation should have failed, but did not")
	}

	// If we specify -overwrite, it will.
	bin.Start("create", "--overwrite", aliceDir, "alice").WaitOrDie(os.Stdout, os.Stderr)
}
<|endoftext|>


<commit_before>package action

import (
	"context"
	"fmt"
	"os"
	"time"

	"github.com/dominikschulz/github-releases/ghrel"
	"github.com/fatih/color"
	"github.com/justwatchcom/gopass/utils/out"
	"github.com/urfave/cli"
)

const (
	gitHubOrg  = "justwatchcom"
	gitHubRepo = "gopass"
)

// Version prints the gopass version
func (s *Action) Version(ctx context.Context, c *cli.Context) error {
	version := make(chan string, 1)
	go func(u chan string) {
		if disabled := os.Getenv("CHECKPOINT_DISABLE"); disabled != "" {
			u <- ""
			return
		}

		if s.version.String() == "0.0.0+HEAD" {
			// chan not check version against HEAD
			u <- ""
			return
		}

		var r ghrel.Release
		var err error
		if len(s.version.Pre) > 0 {
			r, err = ghrel.FetchLatestRelease(gitHubOrg, gitHubRepo)
		} else {
			r, err = ghrel.FetchLatestStableRelease(gitHubOrg, gitHubRepo)
		}
		if err != nil {
			u <- color.RedString("\nError checking latest version: %s", err)
			return
		}

		if s.version.LT(r.Version()) {
			notice := fmt.Sprintf("\nYour version (%s) of gopass is out of date!\nThe latest version is %s.\n", s.version, r.Version().String())
			notice += "You can update by downloading from www.justwatch.com/gopass"
			if err := s.isUpdateable(ctx); err == nil {
				notice += " by running 'gopass update' "
			}
			notice += "or via your package manager"
			u <- color.YellowString(notice)
		}
		u <- ""
	}(version)

	cli.VersionPrinter(c)

	fmt.Printf("  GPG: %s\n", s.Store.GPGVersion(ctx).String())
	fmt.Printf("  Git: %s\n", s.Store.GitVersion(ctx).String())

	select {
	case vi := <-version:
		if vi != "" {
			fmt.Println(vi)
		}
	case <-time.After(2 * time.Second):
		out.Red(ctx, "Version check timed out")
	case <-ctx.Done():
		return s.exitError(ctx, ExitAborted, nil, "user aborted")
	}

	return nil
}
<commit_msg>Fixed string concatenation in Version action (#457)<commit_after>package action

import (
	"context"
	"fmt"
	"os"
	"time"

	"github.com/dominikschulz/github-releases/ghrel"
	"github.com/fatih/color"
	"github.com/justwatchcom/gopass/utils/out"
	"github.com/urfave/cli"
)

const (
	gitHubOrg  = "justwatchcom"
	gitHubRepo = "gopass"
)

// Version prints the gopass version
func (s *Action) Version(ctx context.Context, c *cli.Context) error {
	version := make(chan string, 1)
	go func(u chan string) {
		if disabled := os.Getenv("CHECKPOINT_DISABLE"); disabled != "" {
			u <- ""
			return
		}

		if s.version.String() == "0.0.0+HEAD" {
			// chan not check version against HEAD
			u <- ""
			return
		}

		var r ghrel.Release
		var err error
		if len(s.version.Pre) > 0 {
			r, err = ghrel.FetchLatestRelease(gitHubOrg, gitHubRepo)
		} else {
			r, err = ghrel.FetchLatestStableRelease(gitHubOrg, gitHubRepo)
		}
		if err != nil {
			u <- color.RedString("\nError checking latest version: %s", err)
			return
		}

		if s.version.LT(r.Version()) {
			notice := fmt.Sprintf("\nYour version (%s) of gopass is out of date!\nThe latest version is %s.\n", s.version, r.Version().String())
			notice += "You can update by downloading from www.justwatch.com/gopass"
			if err := s.isUpdateable(ctx); err == nil {
				notice += " by running 'gopass update'"
			}
			notice += " or via your package manager"
			u <- color.YellowString(notice)
		}
		u <- ""
	}(version)

	cli.VersionPrinter(c)

	fmt.Printf("  GPG: %s\n", s.Store.GPGVersion(ctx).String())
	fmt.Printf("  Git: %s\n", s.Store.GitVersion(ctx).String())

	select {
	case vi := <-version:
		if vi != "" {
			fmt.Println(vi)
		}
	case <-time.After(2 * time.Second):
		out.Red(ctx, "Version check timed out")
	case <-ctx.Done():
		return s.exitError(ctx, ExitAborted, nil, "user aborted")
	}

	return nil
}
<|endoftext|>


<commit_before>package mssql

import (
	"database/sql"
	"encoding/hex"
	"log"
	"math"
	"reflect"
	"strings"
	"testing"
	"time"
)

func TestBulkcopy(t *testing.T) {
	t.Skip("TODO: fix this test")
	// TDS level Bulk Insert is not supported on Azure SQL Server.
	if dsn := makeConnStr(t); strings.HasSuffix(strings.Split(dsn.Host, ":")[0], ".database.windows.net") {
		t.Skip("TDS level bulk copy is not supported on Azure SQL Server")
	}
	type testValue struct {
		colname string
		val     interface{}
	}
	tableName := "#table_test"
	geom, _ := hex.DecodeString("E6100000010C00000000000034400000000000004440")
	testValues := []testValue{

		{"test_nvarchar", "ab©ĎéⒻghïjklmnopqЯ☀tuvwxyz"},
		{"test_varchar", "abcdefg"},
		{"test_char", "abcdefg   "},
		{"test_nchar", "abcdefg   "},
		{"test_text", "abcdefg"},
		{"test_ntext", "abcdefg"},
		{"test_float", 1234.56},
		{"test_floatn", 1234.56},
		{"test_real", 1234.56},
		{"test_realn", 1234.56},
		{"test_bit", true},
		{"test_bitn", nil},
		{"test_smalldatetime", time.Date(2010, 11, 12, 13, 14, 0, 0, time.UTC)},
		{"test_smalldatetimen", time.Date(2010, 11, 12, 13, 14, 0, 0, time.UTC)},
		{"test_datetime", time.Date(2010, 11, 12, 13, 14, 15, 120000000, time.UTC)},
		{"test_datetimen", time.Date(2010, 11, 12, 13, 14, 15, 120000000, time.UTC)},
		{"test_datetime2_1", time.Date(2010, 11, 12, 13, 14, 15, 0, time.UTC)},
		{"test_datetime2_3", time.Date(2010, 11, 12, 13, 14, 15, 123000000, time.UTC)},
		{"test_datetime2_7", time.Date(2010, 11, 12, 13, 14, 15, 123000000, time.UTC)},
		{"test_date", time.Date(2010, 11, 12, 00, 00, 00, 0, time.UTC)},
		{"test_tinyint", 255},
		{"test_smallint", 32767},
		{"test_smallintn", nil},
		{"test_int", 2147483647},
		{"test_bigint", 9223372036854775807},
		{"test_bigintn", nil},
		{"test_geom", geom},
		//{"test_smallmoney", nil},
		//{"test_money", nil},
		//{"test_decimal_18_0", nil},
		//{"test_decimal_9_2", nil},
		//{"test_decimal_18_0", nil},
	}

	columns := make([]string, len(testValues))
	for i, val := range testValues {
		columns[i] = val.colname
	}

	values := make([]interface{}, len(testValues))
	for i, val := range testValues {
		values[i] = val.val
	}

	conn := open(t)
	defer conn.Close()

	setupTable(conn, tableName)

	stmt, err := conn.Prepare(CopyIn(tableName, MssqlBulkOptions{}, columns...))

	for i := 0; i < 10; i++ {
		_, err = stmt.Exec(values...)
		if err != nil {
			t.Error("AddRow failed: ", err.Error())
			return
		}
	}

	result, err := stmt.Exec()
	if err != nil {
		t.Fatal("bulkcopy failed: ", err.Error())
	}

	insertedRowCount, _ := result.RowsAffected()
	if insertedRowCount == 0 {
		t.Fatal("0 row inserted!")
	}

	//check that all rows are present
	var rowCount int
	err = conn.QueryRow("select count(*) c from " + tableName).Scan(&rowCount)

	if rowCount != 10 {
		t.Errorf("unexpected row count %d", rowCount)
	}

	//data verification
	rows, err := conn.Query("select " + strings.Join(columns, ",") + " from " + tableName)
	if err != nil {
		log.Fatal(err)
	}
	defer rows.Close()
	for rows.Next() {

		ptrs := make([]interface{}, len(columns))
		container := make([]interface{}, len(columns))
		for i, _ := range ptrs {
			ptrs[i] = &container[i]
		}
		if err := rows.Scan(ptrs...); err != nil {
			t.Fatal(err)
		}
		for i, c := range testValues {
			if !compareValue(container[i], c.val) {
				t.Errorf("columns %s : %s != %s\n", c.colname, container[i], c.val)
			}
		}
	}
	if err := rows.Err(); err != nil {
		t.Error(err)
	}
}

func compareValue(a interface{}, expected interface{}) bool {
	switch expected := expected.(type) {
	case int:
		return int64(expected) == a
	case int32:
		return int64(expected) == a
	case int64:
		return int64(expected) == a
	case float64:
		return math.Abs(expected-a.(float64)) < 0.0001
	default:
		return reflect.DeepEqual(expected, a)
	}
}
func setupTable(conn *sql.DB, tableName string) {

	tablesql := `CREATE TABLE ` + tableName + ` (
	[id] [int] IDENTITY(1,1) NOT NULL,
	[test_nvarchar] [nvarchar](50) NULL,
	[test_varchar] [varchar](50) NULL,
	[test_char] [char](10) NULL,
	[test_nchar] [nchar](10) NULL,
	[test_text] [text] NULL,
	[test_ntext] [ntext] NULL,
	[test_float] [float] NOT NULL,
	[test_floatn] [float] NULL,
	[test_real] [real] NULL,
	[test_realn] [real] NULL,
	[test_bit] [bit] NOT NULL,
	[test_bitn] [bit] NULL,
	[test_smalldatetime] [smalldatetime] NOT NULL,
	[test_smalldatetimen] [smalldatetime] NULL,
	[test_datetime] [datetime] NOT NULL,
	[test_datetimen] [datetime] NULL,
	[test_datetime2_1] [datetime2](1) NULL,
	[test_datetime2_3] [datetime2](3) NULL,
	[test_datetime2_7] [datetime2](7) NULL,
	[test_date] [date] NULL,
	[test_smallmoney] [smallmoney] NULL,
	[test_money] [money] NULL,
	[test_tinyint] [tinyint] NULL,
	[test_smallint] [smallint] NOT NULL,
	[test_smallintn] [smallint] NULL,
	[test_int] [int] NULL,
	[test_bigint] [bigint] NOT NULL,
	[test_bigintn] [bigint] NULL,
	[test_geom] [geometry] NULL,
	[test_geog] [geography] NULL,
	[text_xml] [xml] NULL,
	[test_uniqueidentifier] [uniqueidentifier] NULL,
	[test_decimal_18_0] [decimal](18, 0) NULL,
	[test_decimal_9_2] [decimal](9, 2) NULL,
	[test_decimal_20_0] [decimal](20, 0) NULL,
 CONSTRAINT [PK_` + tableName + `_id] PRIMARY KEY CLUSTERED
(
	[id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY];`
	_, err := conn.Exec(tablesql)
	if err != nil {
		log.Fatal("tablesql failed:", err)
	}

}
<commit_msg>enabled bulkload test<commit_after>package mssql

import (
	"database/sql"
	"encoding/hex"
	"log"
	"math"
	"reflect"
	"strings"
	"testing"
	"time"
)

func TestBulkcopy(t *testing.T) {
	// TDS level Bulk Insert is not supported on Azure SQL Server.
	if dsn := makeConnStr(t); strings.HasSuffix(strings.Split(dsn.Host, ":")[0], ".database.windows.net") {
		t.Skip("TDS level bulk copy is not supported on Azure SQL Server")
	}
	type testValue struct {
		colname string
		val     interface{}
	}
	tableName := "#table_test"
	geom, _ := hex.DecodeString("E6100000010C00000000000034400000000000004440")
	testValues := []testValue{

		{"test_nvarchar", "ab©ĎéⒻghïjklmnopqЯ☀tuvwxyz"},
		{"test_varchar", "abcdefg"},
		{"test_char", "abcdefg   "},
		{"test_nchar", "abcdefg   "},
		{"test_text", "abcdefg"},
		{"test_ntext", "abcdefg"},
		{"test_float", 1234.56},
		{"test_floatn", 1234.56},
		{"test_real", 1234.56},
		{"test_realn", 1234.56},
		{"test_bit", true},
		{"test_bitn", nil},
		{"test_smalldatetime", time.Date(2010, 11, 12, 13, 14, 0, 0, time.UTC)},
		{"test_smalldatetimen", time.Date(2010, 11, 12, 13, 14, 0, 0, time.UTC)},
		{"test_datetime", time.Date(2010, 11, 12, 13, 14, 15, 120000000, time.UTC)},
		{"test_datetimen", time.Date(2010, 11, 12, 13, 14, 15, 120000000, time.UTC)},
		{"test_datetime2_1", time.Date(2010, 11, 12, 13, 14, 15, 0, time.UTC)},
		{"test_datetime2_3", time.Date(2010, 11, 12, 13, 14, 15, 123000000, time.UTC)},
		{"test_datetime2_7", time.Date(2010, 11, 12, 13, 14, 15, 123000000, time.UTC)},
		{"test_date", time.Date(2010, 11, 12, 00, 00, 00, 0, time.UTC)},
		{"test_tinyint", 255},
		{"test_smallint", 32767},
		{"test_smallintn", nil},
		{"test_int", 2147483647},
		{"test_bigint", 9223372036854775807},
		{"test_bigintn", nil},
		{"test_geom", geom},
		//{"test_smallmoney", nil},
		//{"test_money", nil},
		//{"test_decimal_18_0", nil},
		//{"test_decimal_9_2", nil},
		//{"test_decimal_18_0", nil},
	}

	columns := make([]string, len(testValues))
	for i, val := range testValues {
		columns[i] = val.colname
	}

	values := make([]interface{}, len(testValues))
	for i, val := range testValues {
		values[i] = val.val
	}

	conn := open(t)
	defer conn.Close()

	setupTable(conn, tableName)

	stmt, err := conn.Prepare(CopyIn(tableName, MssqlBulkOptions{}, columns...))

	for i := 0; i < 10; i++ {
		_, err = stmt.Exec(values...)
		if err != nil {
			t.Error("AddRow failed: ", err.Error())
			return
		}
	}

	result, err := stmt.Exec()
	if err != nil {
		t.Fatal("bulkcopy failed: ", err.Error())
	}

	insertedRowCount, _ := result.RowsAffected()
	if insertedRowCount == 0 {
		t.Fatal("0 row inserted!")
	}

	//check that all rows are present
	var rowCount int
	err = conn.QueryRow("select count(*) c from " + tableName).Scan(&rowCount)

	if rowCount != 10 {
		t.Errorf("unexpected row count %d", rowCount)
	}

	//data verification
	rows, err := conn.Query("select " + strings.Join(columns, ",") + " from " + tableName)
	if err != nil {
		log.Fatal(err)
	}
	defer rows.Close()
	for rows.Next() {

		ptrs := make([]interface{}, len(columns))
		container := make([]interface{}, len(columns))
		for i, _ := range ptrs {
			ptrs[i] = &container[i]
		}
		if err := rows.Scan(ptrs...); err != nil {
			t.Fatal(err)
		}
		for i, c := range testValues {
			if !compareValue(container[i], c.val) {
				t.Errorf("columns %s : %s != %s\n", c.colname, container[i], c.val)
			}
		}
	}
	if err := rows.Err(); err != nil {
		t.Error(err)
	}
}

func compareValue(a interface{}, expected interface{}) bool {
	switch expected := expected.(type) {
	case int:
		return int64(expected) == a
	case int32:
		return int64(expected) == a
	case int64:
		return int64(expected) == a
	case float64:
		return math.Abs(expected-a.(float64)) < 0.0001
	default:
		return reflect.DeepEqual(expected, a)
	}
}
func setupTable(conn *sql.DB, tableName string) {

	tablesql := `CREATE TABLE ` + tableName + ` (
	[id] [int] IDENTITY(1,1) NOT NULL,
	[test_nvarchar] [nvarchar](50) NULL,
	[test_varchar] [varchar](50) NULL,
	[test_char] [char](10) NULL,
	[test_nchar] [nchar](10) NULL,
	[test_text] [text] NULL,
	[test_ntext] [ntext] NULL,
	[test_float] [float] NOT NULL,
	[test_floatn] [float] NULL,
	[test_real] [real] NULL,
	[test_realn] [real] NULL,
	[test_bit] [bit] NOT NULL,
	[test_bitn] [bit] NULL,
	[test_smalldatetime] [smalldatetime] NOT NULL,
	[test_smalldatetimen] [smalldatetime] NULL,
	[test_datetime] [datetime] NOT NULL,
	[test_datetimen] [datetime] NULL,
	[test_datetime2_1] [datetime2](1) NULL,
	[test_datetime2_3] [datetime2](3) NULL,
	[test_datetime2_7] [datetime2](7) NULL,
	[test_date] [date] NULL,
	[test_smallmoney] [smallmoney] NULL,
	[test_money] [money] NULL,
	[test_tinyint] [tinyint] NULL,
	[test_smallint] [smallint] NOT NULL,
	[test_smallintn] [smallint] NULL,
	[test_int] [int] NULL,
	[test_bigint] [bigint] NOT NULL,
	[test_bigintn] [bigint] NULL,
	[test_geom] [geometry] NULL,
	[test_geog] [geography] NULL,
	[text_xml] [xml] NULL,
	[test_uniqueidentifier] [uniqueidentifier] NULL,
	[test_decimal_18_0] [decimal](18, 0) NULL,
	[test_decimal_9_2] [decimal](9, 2) NULL,
	[test_decimal_20_0] [decimal](20, 0) NULL,
 CONSTRAINT [PK_` + tableName + `_id] PRIMARY KEY CLUSTERED
(
	[id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY];`
	_, err := conn.Exec(tablesql)
	if err != nil {
		log.Fatal("tablesql failed:", err)
	}

}
<|endoftext|>


<commit_before>package edit

import (
	"errors"
	"fmt"

	"github.com/elves/elvish/eval"
	"github.com/elves/elvish/util"
)

// Interface between the editor and elvish script. Implements the le: module.

// makeModule builds a module from an Editor.
func makeModule(ed *Editor) eval.Namespace {
	ns := eval.Namespace{}
	// Populate builtins.
	for _, b := range builtins {
		ns[eval.FnPrefix+b.name] = eval.NewPtrVariable(&BuiltinAsFnValue{b, ed})
	}
	// Populate binding tables in the variable $binding.
	// TODO Make binding specific to the Editor.
	binding := &eval.Struct{
		[]string{"insert", "command", "completion", "navigation", "history"},
		[]eval.Variable{
			eval.NewRoVariable(BindingTable{keyBindings[modeInsert]}),
			eval.NewRoVariable(BindingTable{keyBindings[modeCommand]}),
			eval.NewRoVariable(BindingTable{keyBindings[modeCompletion]}),
			eval.NewRoVariable(BindingTable{keyBindings[modeNavigation]}),
			eval.NewRoVariable(BindingTable{keyBindings[modeHistory]}),
		},
	}

	ns["binding"] = eval.NewRoVariable(binding)
	ns["completer"] = eval.NewRoVariable(CompleterTable(argCompleter))
	ns[eval.FnPrefix+"complete-getopt"] = eval.NewRoVariable(
		// XXX Repr is "&le:complete-getopt" instead of "le:&complete-getopt"
		&eval.BuiltinFn{"le:complete-getopt", eval.WrapFn(complGetopt)})

	ns["prompt"] = ed.ps1
	ns["rprompt"] = ed.rps1
	ns["rprompt-persistent"] = BoolExposer{&ed.rpromptPersistent}
	ns["current-command"] = StringExposer{&ed.line}
	ns["history"] = eval.NewRoVariable(History{&ed.historyMutex, ed.store})

	ns["abbr"] = eval.NewRoVariable(eval.MapStringString(ed.abbreviations))

	ns["before-readline"] = ed.beforeReadLine

	ns[eval.FnPrefix+"styled"] = eval.NewRoVariable(&eval.BuiltinFn{"le:styled", eval.WrapFn(styledBuiltin)})

	return ns
}

func throw(e error) {
	util.Throw(e)
}

func maybeThrow(e error) {
	if e != nil {
		util.Throw(e)
	}
}

func throwf(format string, args ...interface{}) {
	util.Throw(fmt.Errorf(format, args...))
}

// BoolExposer implements eval.Variable and exposes a bool to elvishscript.
type BoolExposer struct {
	valuePtr *bool
}

var errMustBeBool = errors.New("must be bool")

func (be BoolExposer) Set(v eval.Value) {
	if b, ok := v.(eval.Bool); ok {
		*be.valuePtr = bool(b)
	} else {
		throw(errMustBeBool)
	}
}

func (be BoolExposer) Get() eval.Value {
	return eval.Bool(*be.valuePtr)
}

// StringExposer implements eval.Variable and exposes a string to elvishscript.

type StringExposer struct {
	valuePtr *string
}

var errMustBeString = errors.New("must be string")

func (se StringExposer) Set(v eval.Value) {
	if s, ok := v.(eval.String); ok {
		*se.valuePtr = string(s)
	} else {
		throw(errMustBeString)
	}
}

func (se StringExposer) Get() eval.Value {
	return eval.String(*se.valuePtr)
}
<commit_msg>Setting $le:current-command always moves the cursor to the end.<commit_after>package edit

import (
	"errors"
	"fmt"

	"github.com/elves/elvish/eval"
	"github.com/elves/elvish/util"
)

// Interface between the editor and elvish script. Implements the le: module.

// makeModule builds a module from an Editor.
func makeModule(ed *Editor) eval.Namespace {
	ns := eval.Namespace{}
	// Populate builtins.
	for _, b := range builtins {
		ns[eval.FnPrefix+b.name] = eval.NewPtrVariable(&BuiltinAsFnValue{b, ed})
	}
	// Populate binding tables in the variable $binding.
	// TODO Make binding specific to the Editor.
	binding := &eval.Struct{
		[]string{"insert", "command", "completion", "navigation", "history"},
		[]eval.Variable{
			eval.NewRoVariable(BindingTable{keyBindings[modeInsert]}),
			eval.NewRoVariable(BindingTable{keyBindings[modeCommand]}),
			eval.NewRoVariable(BindingTable{keyBindings[modeCompletion]}),
			eval.NewRoVariable(BindingTable{keyBindings[modeNavigation]}),
			eval.NewRoVariable(BindingTable{keyBindings[modeHistory]}),
		},
	}

	ns["binding"] = eval.NewRoVariable(binding)
	ns["completer"] = eval.NewRoVariable(CompleterTable(argCompleter))
	ns[eval.FnPrefix+"complete-getopt"] = eval.NewRoVariable(
		// XXX Repr is "&le:complete-getopt" instead of "le:&complete-getopt"
		&eval.BuiltinFn{"le:complete-getopt", eval.WrapFn(complGetopt)})

	ns["prompt"] = ed.ps1
	ns["rprompt"] = ed.rps1
	ns["rprompt-persistent"] = BoolExposer{&ed.rpromptPersistent}
	ns["current-command"] = LineExposer{ed}
	ns["history"] = eval.NewRoVariable(History{&ed.historyMutex, ed.store})

	ns["abbr"] = eval.NewRoVariable(eval.MapStringString(ed.abbreviations))

	ns["before-readline"] = ed.beforeReadLine

	ns[eval.FnPrefix+"styled"] = eval.NewRoVariable(&eval.BuiltinFn{"le:styled", eval.WrapFn(styledBuiltin)})

	return ns
}

func throw(e error) {
	util.Throw(e)
}

func maybeThrow(e error) {
	if e != nil {
		util.Throw(e)
	}
}

func throwf(format string, args ...interface{}) {
	util.Throw(fmt.Errorf(format, args...))
}

// BoolExposer implements eval.Variable and exposes a bool to elvishscript.
type BoolExposer struct {
	valuePtr *bool
}

var errMustBeBool = errors.New("must be bool")

func (be BoolExposer) Set(v eval.Value) {
	if b, ok := v.(eval.Bool); ok {
		*be.valuePtr = bool(b)
	} else {
		throw(errMustBeBool)
	}
}

func (be BoolExposer) Get() eval.Value {
	return eval.Bool(*be.valuePtr)
}

// StringExposer implements eval.Variable and exposes a string to elvishscript.

type StringExposer struct {
	valuePtr *string
}

var errMustBeString = errors.New("must be string")

func (se StringExposer) Set(v eval.Value) {
	if s, ok := v.(eval.String); ok {
		*se.valuePtr = string(s)
	} else {
		throw(errMustBeString)
	}
}

func (se StringExposer) Get() eval.Value {
	return eval.String(*se.valuePtr)
}

// LineExposer exposes ed.line to elvishscript.

type LineExposer struct {
	ed *Editor
}

func (l LineExposer) Set(v eval.Value) {
	if s, ok := v.(eval.String); ok {
		l.ed.line = string(s)
		l.ed.dot = len(l.ed.line)
	} else {
		throw(errMustBeString)
	}
}

func (l LineExposer) Get() eval.Value {
	return eval.String(l.ed.line)
}
<|endoftext|>