<commit_before>// Copyright 2015 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 samples

import (
	"bytes"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path"
	"sync"

	"github.com/jacobsa/ogletest"
	"golang.org/x/net/context"
)

var fToolPath = flag.String(
	"mount_sample",
	"",
	"Path to the mount_sample tool. If unset, we will compile it.")

// A struct that implements common behavior needed by tests in the samples/
// directory where the file system is mounted by a subprocess. Use it as an
// embedded field in your test fixture, calling its SetUp method from your
// SetUp method after setting the MountType and MountFlags fields.
type SubprocessTest struct {
	// The type of the file system to mount. Must be recognized by mount_sample.
	MountType string

	// Additional flags to be passed to the mount_sample tool.
	MountFlags []string

	// A list of files to pass to mount_sample. The given string flag will be
	// used to pass the file descriptor number.
	MountFiles map[string]*os.File

	// A context object that can be used for long-running operations.
	Ctx context.Context

	// The directory at which the file system is mounted.
	Dir string

	// Anothing non-nil in this slice will be closed by TearDown. The test will
	// fail if closing fails.
	ToClose []io.Closer

	mountSampleErr <-chan error
}

// Mount the file system and initialize the other exported fields of the
// struct. Panics on error.
//
// REQUIRES: t.FileSystem has been set.
func (t *SubprocessTest) SetUp(ti *ogletest.TestInfo) {
	err := t.initialize()
	if err != nil {
		panic(err)
	}
}

// Private state for getToolPath.
var getToolContents_Contents []byte
var getToolContents_Err error
var getToolContents_Once sync.Once

// Implementation detail of getToolPath.
func getToolContentsImpl() (contents []byte, err error) {
	// Fast path: has the user set the flag?
	if *fToolPath != "" {
		contents, err = ioutil.ReadFile(*fToolPath)
		if err != nil {
			err = fmt.Errorf("Reading mount_sample contents: %v", err)
			return
		}

		return
	}

	// Create a temporary directory into which we will compile the tool.
	tempDir, err := ioutil.TempDir("", "sample_test")
	if err != nil {
		err = fmt.Errorf("TempDir: %v", err)
		return
	}

	toolPath := path.Join(tempDir, "mount_sample")

	// Ensure that we kill the temporary directory when we're finished here.
	defer os.RemoveAll(tempDir)

	// Run "go build".
	cmd := exec.Command(
		"go",
		"build",
		"-o",
		toolPath,
		"github.com/jacobsa/fuse/samples/mount_sample")

	output, err := cmd.CombinedOutput()
	if err != nil {
		err = fmt.Errorf(
			"mount_sample exited with %v, output:\n%s",
			err,
			string(output))

		return
	}

	// Slurp the tool contents.
	contents, err = ioutil.ReadFile(toolPath)
	if err != nil {
		err = fmt.Errorf("ReadFile: %v", err)
		return
	}

	return
}

// Build the mount_sample tool if it has not yet been built for this process.
// Return its contents.
func getToolContents() (contents []byte, err error) {
	// Get hold of the binary contents, if we haven't yet.
	getToolContents_Once.Do(func() {
		getToolContents_Contents, getToolContents_Err = getToolContentsImpl()
	})

	contents, err = getToolContents_Contents, getToolContents_Err
	return
}

func waitForMountSample(
	cmd *exec.Cmd,
	errChan chan<- error,
	stderr *bytes.Buffer) {
	// However we exit, write the error to the channel.
	var err error
	defer func() {
		errChan <- err
	}()

	// Wait for the command.
	err = cmd.Wait()
	if err == nil {
		return
	}

	// Make exit errors nicer.
	if exitErr, ok := err.(*exec.ExitError); ok {
		err = fmt.Errorf(
			"mount_sample exited with %v. Stderr:\n%s",
			exitErr,
			stderr.String())

		return
	}

	err = fmt.Errorf("Waiting for mount_sample: %v", err)
}

func waitForReady(readyReader *os.File, c chan<- struct{}) {
	_, err := readyReader.Read(make([]byte, 1))
	if err != nil {
		log.Printf("Readying from ready pipe: %v", err)
		return
	}

	c <- struct{}{}
}

// Like SetUp, but doens't panic.
func (t *SubprocessTest) initialize() (err error) {
	// Initialize the context.
	t.Ctx = context.Background()

	// Set up a temporary directory.
	t.Dir, err = ioutil.TempDir("", "sample_test")
	if err != nil {
		err = fmt.Errorf("TempDir: %v", err)
		return
	}

	// Build/read the mount_sample tool.
	toolContents, err := getToolContents()
	if err != nil {
		err = fmt.Errorf("getTooltoolContents: %v", err)
		return
	}

	// Create a temporary file to hold the contents of the tool.
	toolFile, err := ioutil.TempFile("", "sample_test")
	if err != nil {
		err = fmt.Errorf("TempFile: %v", err)
		return
	}

	defer toolFile.Close()

	// Ensure that it is deleted when we leave.
	toolPath := toolFile.Name()
	defer os.Remove(toolPath)

	// Write out the tool contents and make them executable.
	if _, err = toolFile.Write(toolContents); err != nil {
		err = fmt.Errorf("toolFile.Write: %v", err)
		return
	}

	if err = toolFile.Chmod(0500); err != nil {
		err = fmt.Errorf("toolFile.Chmod: %v", err)
		return
	}

	// Close the tool file to prevent "text file busy" errors below.
	err = toolFile.Close()
	toolFile = nil
	if err != nil {
		err = fmt.Errorf("toolFile.Close: %v", err)
		return
	}

	// Set up basic args for the subprocess.
	args := []string{
		"--type",
		t.MountType,
		"--mount_point",
		t.Dir,
	}

	args = append(args, t.MountFlags...)

	// Set up a pipe for the "ready" status.
	readyReader, readyWriter, err := os.Pipe()
	if err != nil {
		err = fmt.Errorf("Pipe: %v", err)
		return
	}

	defer readyReader.Close()
	defer readyWriter.Close()

	t.MountFiles["ready_file"] = readyWriter

	// Set up inherited files and appropriate flags.
	var extraFiles []*os.File
	for flag, file := range t.MountFiles {
		// Cf. os/exec.Cmd.ExtraFiles
		fd := 3 + len(extraFiles)

		extraFiles = append(extraFiles, file)
		args = append(args, "--"+flag)
		args = append(args, fmt.Sprintf("%d", fd))
	}

	// Set up a command.
	var stderr bytes.Buffer
	mountCmd := exec.Command(toolPath, args...)
	mountCmd.Stderr = &stderr
	mountCmd.ExtraFiles = extraFiles

	// Start it.
	if err = mountCmd.Start(); err != nil {
		err = fmt.Errorf("mountCmd.Start: %v", err)
		return
	}

	// Launch a goroutine that waits for it and returns its status.
	mountSampleErr := make(chan error, 1)
	go waitForMountSample(mountCmd, mountSampleErr, &stderr)

	// Wait for the tool to say the file system is ready. In parallel, watch for
	// the tool to fail.
	readyChan := make(chan struct{}, 1)
	go waitForReady(readyReader, readyChan)

	select {
	case <-readyChan:
	case err = <-mountSampleErr:
		return
	}

	// TearDown is no responsible for joining.
	t.mountSampleErr = mountSampleErr

	return
}

// Unmount the file system and clean up. Panics on error.
func (t *SubprocessTest) TearDown() {
	err := t.destroy()
	if err != nil {
		panic(err)
	}
}

// Like TearDown, but doesn't panic.
func (t *SubprocessTest) destroy() (err error) {
	// Make sure we clean up after ourselves after everything else below.

	// Close what is necessary.
	for _, c := range t.ToClose {
		if c == nil {
			continue
		}

		ogletest.ExpectEq(nil, c.Close())
	}

	// If we didn't try to mount the file system, there's nothing further to do.
	if t.mountSampleErr == nil {
		return
	}

	// In the background, initiate an unmount.
	unmountErrChan := make(chan error)
	go func() {
		unmountErrChan <- unmount(t.Dir)
	}()

	// Make sure we wait for the unmount, even if we've already returned early in
	// error. Return its error if we haven't seen any other error.
	defer func() {
		// Wait.
		unmountErr := <-unmountErrChan
		if unmountErr != nil {
			if err != nil {
				log.Println("unmount:", unmountErr)
				return
			}

			err = fmt.Errorf("unmount: %v", unmountErr)
			return
		}

		// Clean up.
		ogletest.ExpectEq(nil, os.Remove(t.Dir))
	}()

	// Wait for the subprocess.
	if err = <-t.mountSampleErr; err != nil {
		return
	}

	return
}
<commit_msg>Added a --debug flag.<commit_after>// Copyright 2015 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 samples

import (
	"bytes"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path"
	"sync"

	"github.com/jacobsa/ogletest"
	"golang.org/x/net/context"
)

var fToolPath = flag.String(
	"mount_sample",
	"",
	"Path to the mount_sample tool. If unset, we will compile it.")

var fDebug = flag.Bool("debug", false, "If true, print fuse debug info.")

// A struct that implements common behavior needed by tests in the samples/
// directory where the file system is mounted by a subprocess. Use it as an
// embedded field in your test fixture, calling its SetUp method from your
// SetUp method after setting the MountType and MountFlags fields.
type SubprocessTest struct {
	// The type of the file system to mount. Must be recognized by mount_sample.
	MountType string

	// Additional flags to be passed to the mount_sample tool.
	MountFlags []string

	// A list of files to pass to mount_sample. The given string flag will be
	// used to pass the file descriptor number.
	MountFiles map[string]*os.File

	// A context object that can be used for long-running operations.
	Ctx context.Context

	// The directory at which the file system is mounted.
	Dir string

	// Anothing non-nil in this slice will be closed by TearDown. The test will
	// fail if closing fails.
	ToClose []io.Closer

	mountSampleErr <-chan error
}

// Mount the file system and initialize the other exported fields of the
// struct. Panics on error.
//
// REQUIRES: t.FileSystem has been set.
func (t *SubprocessTest) SetUp(ti *ogletest.TestInfo) {
	err := t.initialize()
	if err != nil {
		panic(err)
	}
}

// Private state for getToolPath.
var getToolContents_Contents []byte
var getToolContents_Err error
var getToolContents_Once sync.Once

// Implementation detail of getToolPath.
func getToolContentsImpl() (contents []byte, err error) {
	// Fast path: has the user set the flag?
	if *fToolPath != "" {
		contents, err = ioutil.ReadFile(*fToolPath)
		if err != nil {
			err = fmt.Errorf("Reading mount_sample contents: %v", err)
			return
		}

		return
	}

	// Create a temporary directory into which we will compile the tool.
	tempDir, err := ioutil.TempDir("", "sample_test")
	if err != nil {
		err = fmt.Errorf("TempDir: %v", err)
		return
	}

	toolPath := path.Join(tempDir, "mount_sample")

	// Ensure that we kill the temporary directory when we're finished here.
	defer os.RemoveAll(tempDir)

	// Run "go build".
	cmd := exec.Command(
		"go",
		"build",
		"-o",
		toolPath,
		"github.com/jacobsa/fuse/samples/mount_sample")

	output, err := cmd.CombinedOutput()
	if err != nil {
		err = fmt.Errorf(
			"mount_sample exited with %v, output:\n%s",
			err,
			string(output))

		return
	}

	// Slurp the tool contents.
	contents, err = ioutil.ReadFile(toolPath)
	if err != nil {
		err = fmt.Errorf("ReadFile: %v", err)
		return
	}

	return
}

// Build the mount_sample tool if it has not yet been built for this process.
// Return its contents.
func getToolContents() (contents []byte, err error) {
	// Get hold of the binary contents, if we haven't yet.
	getToolContents_Once.Do(func() {
		getToolContents_Contents, getToolContents_Err = getToolContentsImpl()
	})

	contents, err = getToolContents_Contents, getToolContents_Err
	return
}

func waitForMountSample(
	cmd *exec.Cmd,
	errChan chan<- error,
	stderr *bytes.Buffer) {
	// However we exit, write the error to the channel.
	var err error
	defer func() {
		errChan <- err
	}()

	// Wait for the command.
	err = cmd.Wait()
	if err == nil {
		return
	}

	// Make exit errors nicer.
	if exitErr, ok := err.(*exec.ExitError); ok {
		err = fmt.Errorf(
			"mount_sample exited with %v. Stderr:\n%s",
			exitErr,
			stderr.String())

		return
	}

	err = fmt.Errorf("Waiting for mount_sample: %v", err)
}

func waitForReady(readyReader *os.File, c chan<- struct{}) {
	_, err := readyReader.Read(make([]byte, 1))
	if err != nil {
		log.Printf("Readying from ready pipe: %v", err)
		return
	}

	c <- struct{}{}
}

// Like SetUp, but doens't panic.
func (t *SubprocessTest) initialize() (err error) {
	// Initialize the context.
	t.Ctx = context.Background()

	// Set up a temporary directory.
	t.Dir, err = ioutil.TempDir("", "sample_test")
	if err != nil {
		err = fmt.Errorf("TempDir: %v", err)
		return
	}

	// Build/read the mount_sample tool.
	toolContents, err := getToolContents()
	if err != nil {
		err = fmt.Errorf("getTooltoolContents: %v", err)
		return
	}

	// Create a temporary file to hold the contents of the tool.
	toolFile, err := ioutil.TempFile("", "sample_test")
	if err != nil {
		err = fmt.Errorf("TempFile: %v", err)
		return
	}

	defer toolFile.Close()

	// Ensure that it is deleted when we leave.
	toolPath := toolFile.Name()
	defer os.Remove(toolPath)

	// Write out the tool contents and make them executable.
	if _, err = toolFile.Write(toolContents); err != nil {
		err = fmt.Errorf("toolFile.Write: %v", err)
		return
	}

	if err = toolFile.Chmod(0500); err != nil {
		err = fmt.Errorf("toolFile.Chmod: %v", err)
		return
	}

	// Close the tool file to prevent "text file busy" errors below.
	err = toolFile.Close()
	toolFile = nil
	if err != nil {
		err = fmt.Errorf("toolFile.Close: %v", err)
		return
	}

	// Set up basic args for the subprocess.
	args := []string{
		"--type",
		t.MountType,
		"--mount_point",
		t.Dir,
	}

	args = append(args, t.MountFlags...)

	// Set up a pipe for the "ready" status.
	readyReader, readyWriter, err := os.Pipe()
	if err != nil {
		err = fmt.Errorf("Pipe: %v", err)
		return
	}

	defer readyReader.Close()
	defer readyWriter.Close()

	t.MountFiles["ready_file"] = readyWriter

	// Set up inherited files and appropriate flags.
	var extraFiles []*os.File
	for flag, file := range t.MountFiles {
		// Cf. os/exec.Cmd.ExtraFiles
		fd := 3 + len(extraFiles)

		extraFiles = append(extraFiles, file)
		args = append(args, "--"+flag)
		args = append(args, fmt.Sprintf("%d", fd))
	}

	// Set up a command.
	var stderr bytes.Buffer
	mountCmd := exec.Command(toolPath, args...)
	mountCmd.Stderr = &stderr
	mountCmd.ExtraFiles = extraFiles

	// Handle debug mode.
	if *fDebug {
		mountCmd.Stderr = os.Stderr
		mountCmd.Args = append(mountCmd.Args, "--fuse.debug")
	}

	// Start the command.
	if err = mountCmd.Start(); err != nil {
		err = fmt.Errorf("mountCmd.Start: %v", err)
		return
	}

	// Launch a goroutine that waits for it and returns its status.
	mountSampleErr := make(chan error, 1)
	go waitForMountSample(mountCmd, mountSampleErr, &stderr)

	// Wait for the tool to say the file system is ready. In parallel, watch for
	// the tool to fail.
	readyChan := make(chan struct{}, 1)
	go waitForReady(readyReader, readyChan)

	select {
	case <-readyChan:
	case err = <-mountSampleErr:
		return
	}

	// TearDown is no responsible for joining.
	t.mountSampleErr = mountSampleErr

	return
}

// Unmount the file system and clean up. Panics on error.
func (t *SubprocessTest) TearDown() {
	err := t.destroy()
	if err != nil {
		panic(err)
	}
}

// Like TearDown, but doesn't panic.
func (t *SubprocessTest) destroy() (err error) {
	// Make sure we clean up after ourselves after everything else below.

	// Close what is necessary.
	for _, c := range t.ToClose {
		if c == nil {
			continue
		}

		ogletest.ExpectEq(nil, c.Close())
	}

	// If we didn't try to mount the file system, there's nothing further to do.
	if t.mountSampleErr == nil {
		return
	}

	// In the background, initiate an unmount.
	unmountErrChan := make(chan error)
	go func() {
		unmountErrChan <- unmount(t.Dir)
	}()

	// Make sure we wait for the unmount, even if we've already returned early in
	// error. Return its error if we haven't seen any other error.
	defer func() {
		// Wait.
		unmountErr := <-unmountErrChan
		if unmountErr != nil {
			if err != nil {
				log.Println("unmount:", unmountErr)
				return
			}

			err = fmt.Errorf("unmount: %v", unmountErr)
			return
		}

		// Clean up.
		ogletest.ExpectEq(nil, os.Remove(t.Dir))
	}()

	// Wait for the subprocess.
	if err = <-t.mountSampleErr; err != nil {
		return
	}

	return
}
<|endoftext|>


<commit_before>// Copyright 2012-2014 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package charm_test

import (
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"io/ioutil"
	"net/url"
	"os"
	"path/filepath"
	"time"

	gitjujutesting "github.com/juju/testing"
	jc "github.com/juju/testing/checkers"
	"github.com/juju/utils"
	gc "gopkg.in/check.v1"
	corecharm "gopkg.in/juju/charm.v4"
	charmtesting "gopkg.in/juju/charm.v4/testing"

	"github.com/juju/juju/api"
	"github.com/juju/juju/api/uniter"
	"github.com/juju/juju/juju/testing"
	"github.com/juju/juju/state"
	coretesting "github.com/juju/juju/testing"
	"github.com/juju/juju/worker/uniter/charm"
)

type BundlesDirSuite struct {
	gitjujutesting.HTTPSuite
	testing.JujuConnSuite

	st     *api.State
	uniter *uniter.State
}

var _ = gc.Suite(&BundlesDirSuite{})

func (s *BundlesDirSuite) SetUpSuite(c *gc.C) {
	s.HTTPSuite.SetUpSuite(c)
	s.JujuConnSuite.SetUpSuite(c)
}

func (s *BundlesDirSuite) TearDownSuite(c *gc.C) {
	s.JujuConnSuite.TearDownSuite(c)
	s.HTTPSuite.TearDownSuite(c)
}

func (s *BundlesDirSuite) SetUpTest(c *gc.C) {
	s.HTTPSuite.SetUpTest(c)
	s.JujuConnSuite.SetUpTest(c)

	// Add a charm, service and unit to login to the API with.
	charm := s.AddTestingCharm(c, "wordpress")
	service := s.AddTestingService(c, "wordpress", charm)
	unit, err := service.AddUnit()
	c.Assert(err, gc.IsNil)
	password, err := utils.RandomPassword()
	c.Assert(err, gc.IsNil)
	err = unit.SetPassword(password)
	c.Assert(err, gc.IsNil)

	s.st = s.OpenAPIAs(c, unit.Tag(), password)
	c.Assert(s.st, gc.NotNil)
	s.uniter, err = s.st.Uniter()
	c.Assert(err, gc.IsNil)
	c.Assert(s.uniter, gc.NotNil)
}

func (s *BundlesDirSuite) TearDownTest(c *gc.C) {
	err := s.st.Close()
	c.Assert(err, gc.IsNil)
	s.JujuConnSuite.TearDownTest(c)
	s.HTTPSuite.TearDownTest(c)
}

func (s *BundlesDirSuite) AddCharm(c *gc.C) (charm.BundleInfo, *state.Charm, []byte) {
	curl := corecharm.MustParseURL("cs:quantal/dummy-1")
	storagePath := "dummy-1"
	bunpath := charmtesting.Charms.CharmArchivePath(c.MkDir(), "dummy")
	bun, err := corecharm.ReadCharmArchive(bunpath)
	c.Assert(err, gc.IsNil)
	bundata, hash := readHash(c, bunpath)
	sch, err := s.State.AddCharm(bun, curl, storagePath, hash)
	c.Assert(err, gc.IsNil)
	apiCharm, err := s.uniter.Charm(sch.URL())
	c.Assert(err, gc.IsNil)

	surl, err := url.Parse(s.URL("/some/charm.bundle"))
	c.Assert(err, gc.IsNil)
	mock := &mockArchiveURLCharm{apiCharm, surl}
	return mock, sch, bundata
}

type mockArchiveURLCharm struct {
	charm.BundleInfo
	archiveURL *url.URL
}

func (i *mockArchiveURLCharm) ArchiveURL() *url.URL {
	return i.archiveURL
}

func (s *BundlesDirSuite) TestGet(c *gc.C) {
	basedir := c.MkDir()
	bunsdir := filepath.Join(basedir, "random", "bundles")
	d := charm.NewBundlesDir(bunsdir)

	// Check it doesn't get created until it's needed.
	_, err := os.Stat(bunsdir)
	c.Assert(err, jc.Satisfies, os.IsNotExist)

	// Add a charm to state that we can try to get.
	apiCharm, sch, bundata := s.AddCharm(c)

	// Try to get the charm when the content doesn't match.
	gitjujutesting.Server.Response(200, nil, []byte("roflcopter"))
	_, err = d.Read(apiCharm, nil)
	prefix := fmt.Sprintf(`failed to download charm "cs:quantal/dummy-1" from %q: `, apiCharm.ArchiveURL())
	c.Assert(err, gc.ErrorMatches, prefix+fmt.Sprintf(`expected sha256 %q, got ".*"`, sch.BundleSha256()))

	// Try to get a charm whose bundle doesn't exist.
	gitjujutesting.Server.Response(404, nil, nil)
	_, err = d.Read(apiCharm, nil)
	c.Assert(err, gc.ErrorMatches, prefix+`.* 404 Not Found`)

	// Get a charm whose bundle exists and whose content matches.
	gitjujutesting.Server.Response(200, nil, bundata)
	ch, err := d.Read(apiCharm, nil)
	c.Assert(err, gc.IsNil)
	assertCharm(c, ch, sch)

	// Get the same charm again, without preparing a response from the server.
	ch, err = d.Read(apiCharm, nil)
	c.Assert(err, gc.IsNil)
	assertCharm(c, ch, sch)

	// Abort a download.
	err = os.RemoveAll(bunsdir)
	c.Assert(err, gc.IsNil)
	abort := make(chan struct{})
	done := make(chan bool)
	go func() {
		ch, err := d.Read(apiCharm, abort)
		c.Assert(ch, gc.IsNil)
		c.Assert(err, gc.ErrorMatches, prefix+"aborted")
		close(done)
	}()
	close(abort)
	gitjujutesting.Server.Response(500, nil, nil)
	select {
	case <-done:
	case <-time.After(coretesting.LongWait):
		c.Fatalf("timed out waiting for abort")
	}
}

func readHash(c *gc.C, path string) ([]byte, string) {
	data, err := ioutil.ReadFile(path)
	c.Assert(err, gc.IsNil)
	hash := sha256.New()
	hash.Write(data)
	return data, hex.EncodeToString(hash.Sum(nil))
}

func assertCharm(c *gc.C, bun charm.Bundle, sch *state.Charm) {
	actual := bun.(*corecharm.CharmArchive)
	c.Assert(actual.Revision(), gc.Equals, sch.Revision())
	c.Assert(actual.Meta(), gc.DeepEquals, sch.Meta())
	c.Assert(actual.Config(), gc.DeepEquals, sch.Config())
}
<commit_msg>silence stupid warning on push<commit_after>// Copyright 2012-2014 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package charm_test

import (
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"io/ioutil"
	"net/url"
	"os"
	"path/filepath"
	"time"

	gitjujutesting "github.com/juju/testing"
	jc "github.com/juju/testing/checkers"
	"github.com/juju/utils"
	gc "gopkg.in/check.v1"
	corecharm "gopkg.in/juju/charm.v4"
	charmtesting "gopkg.in/juju/charm.v4/testing"

	"github.com/juju/juju/api"
	"github.com/juju/juju/api/uniter"
	"github.com/juju/juju/juju/testing"
	"github.com/juju/juju/state"
	coretesting "github.com/juju/juju/testing"
	"github.com/juju/juju/worker/uniter/charm"
)

type BundlesDirSuite struct {
	gitjujutesting.HTTPSuite
	testing.JujuConnSuite

	st     *api.State
	uniter *uniter.State
}

var _ = gc.Suite(&BundlesDirSuite{})

func (s *BundlesDirSuite) SetUpSuite(c *gc.C) {
	s.HTTPSuite.SetUpSuite(c)
	s.JujuConnSuite.SetUpSuite(c)
}

func (s *BundlesDirSuite) TearDownSuite(c *gc.C) {
	s.JujuConnSuite.TearDownSuite(c)
	s.HTTPSuite.TearDownSuite(c)
}

func (s *BundlesDirSuite) SetUpTest(c *gc.C) {
	s.HTTPSuite.SetUpTest(c)
	s.JujuConnSuite.SetUpTest(c)

	// Add a charm, service and unit to login to the API with.
	charm := s.AddTestingCharm(c, "wordpress")
	service := s.AddTestingService(c, "wordpress", charm)
	unit, err := service.AddUnit()
	c.Assert(err, gc.IsNil)
	password, err := utils.RandomPassword()
	c.Assert(err, gc.IsNil)
	err = unit.SetPassword(password)
	c.Assert(err, gc.IsNil)

	s.st = s.OpenAPIAs(c, unit.Tag(), password)
	c.Assert(s.st, gc.NotNil)
	s.uniter, err = s.st.Uniter()
	c.Assert(err, gc.IsNil)
	c.Assert(s.uniter, gc.NotNil)
}

func (s *BundlesDirSuite) TearDownTest(c *gc.C) {
	err := s.st.Close()
	c.Assert(err, gc.IsNil)
	s.JujuConnSuite.TearDownTest(c)
	s.HTTPSuite.TearDownTest(c)
}

func (s *BundlesDirSuite) AddCharm(c *gc.C) (charm.BundleInfo, *state.Charm, []byte) {
	curl := corecharm.MustParseURL("cs:quantal/dummy-1")
	storagePath := "dummy-1"
	bunpath := charmtesting.Charms.CharmArchivePath(c.MkDir(), "dummy")
	bun, err := corecharm.ReadCharmArchive(bunpath)
	c.Assert(err, gc.IsNil)
	bundata, hash := readHash(c, bunpath)
	sch, err := s.State.AddCharm(bun, curl, storagePath, hash)
	c.Assert(err, gc.IsNil)
	apiCharm, err := s.uniter.Charm(sch.URL())
	c.Assert(err, gc.IsNil)

	surl, err := url.Parse(s.URL("/some/charm.bundle"))
	c.Assert(err, gc.IsNil)
	mock := &mockArchiveURLCharm{apiCharm, surl}
	return mock, sch, bundata
}

type mockArchiveURLCharm struct {
	charm.BundleInfo
	archiveURL *url.URL
}

func (i *mockArchiveURLCharm) ArchiveURL() *url.URL {
	return i.archiveURL
}

func (s *BundlesDirSuite) TestGet(c *gc.C) {
	basedir := c.MkDir()
	bunsdir := filepath.Join(basedir, "random", "bundles")
	d := charm.NewBundlesDir(bunsdir)

	// Check it doesn't get created until it's needed.
	_, err := os.Stat(bunsdir)
	c.Assert(err, jc.Satisfies, os.IsNotExist)

	// Add a charm to state that we can try to get.
	apiCharm, sch, bundata := s.AddCharm(c)

	// Try to get the charm when the content doesn't match.
	gitjujutesting.Server.Response(200, nil, []byte("roflcopter"))
	_, err = d.Read(apiCharm, nil)
	prefix := fmt.Sprintf(`failed to download charm "cs:quantal/dummy-1" from "%s": `, apiCharm.ArchiveURL())
	c.Assert(err, gc.ErrorMatches, prefix+fmt.Sprintf(`expected sha256 %q, got ".*"`, sch.BundleSha256()))

	// Try to get a charm whose bundle doesn't exist.
	gitjujutesting.Server.Response(404, nil, nil)
	_, err = d.Read(apiCharm, nil)
	c.Assert(err, gc.ErrorMatches, prefix+`.* 404 Not Found`)

	// Get a charm whose bundle exists and whose content matches.
	gitjujutesting.Server.Response(200, nil, bundata)
	ch, err := d.Read(apiCharm, nil)
	c.Assert(err, gc.IsNil)
	assertCharm(c, ch, sch)

	// Get the same charm again, without preparing a response from the server.
	ch, err = d.Read(apiCharm, nil)
	c.Assert(err, gc.IsNil)
	assertCharm(c, ch, sch)

	// Abort a download.
	err = os.RemoveAll(bunsdir)
	c.Assert(err, gc.IsNil)
	abort := make(chan struct{})
	done := make(chan bool)
	go func() {
		ch, err := d.Read(apiCharm, abort)
		c.Assert(ch, gc.IsNil)
		c.Assert(err, gc.ErrorMatches, prefix+"aborted")
		close(done)
	}()
	close(abort)
	gitjujutesting.Server.Response(500, nil, nil)
	select {
	case <-done:
	case <-time.After(coretesting.LongWait):
		c.Fatalf("timed out waiting for abort")
	}
}

func readHash(c *gc.C, path string) ([]byte, string) {
	data, err := ioutil.ReadFile(path)
	c.Assert(err, gc.IsNil)
	hash := sha256.New()
	hash.Write(data)
	return data, hex.EncodeToString(hash.Sum(nil))
}

func assertCharm(c *gc.C, bun charm.Bundle, sch *state.Charm) {
	actual := bun.(*corecharm.CharmArchive)
	c.Assert(actual.Revision(), gc.Equals, sch.Revision())
	c.Assert(actual.Meta(), gc.DeepEquals, sch.Meta())
	c.Assert(actual.Config(), gc.DeepEquals, sch.Config())
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"os"
	"strconv"
	"strings"

	"github.com/mtyurt/slackbet"
	"github.com/mtyurt/slackbet/bet"
	"github.com/mtyurt/slackbet/repo"
	"github.com/mtyurt/slackbet/slack"
)

const availableCommands = "Available commands: save, start, end, list, info, whowins"

func betHandler(service slackbet.BetService) func(w http.ResponseWriter, r *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		err := service.ParseRequestAndCheckToken(r)
		if err != nil {
			writeResponseWithBadRequest(&w, err.Error())
			return
		}
		user := r.FormValue("user_name")
		text := r.FormValue("text")
		commands := strings.Split(text, " ")
		if commands == nil || len(commands) < 1 {
			writeResponseWithBadRequest(&w, availableCommands)
			return
		}
		firstCommand := commands[0]
		if !strings.EqualFold("save", firstCommand) &&
			!strings.EqualFold("start", firstCommand) &&
			!strings.EqualFold("end", firstCommand) &&
			!strings.EqualFold("whowins", firstCommand) &&
			!strings.EqualFold("savefor", firstCommand) &&
			!strings.EqualFold("listabsent", firstCommand) &&
			!strings.EqualFold("savewinner", firstCommand) &&
			!strings.EqualFold("list", firstCommand) &&
			!strings.EqualFold("info", firstCommand) {
			writeResponseWithBadRequest(&w, availableCommands)
			return
		}
		var resp string
		if strings.EqualFold("start", firstCommand) {
			resp, err = service.StartNewBet(user)
		} else if strings.EqualFold("list", firstCommand) {
			resp, err = service.ListBets()
		} else if strings.EqualFold("save", firstCommand) {
			if len(commands) != 2 {
				writeResponseWithBadRequest(&w, "save command format: save <number>")
				return
			}
			number, err := strconv.Atoi(commands[1])
			if err != nil {
				writeResponseWithBadRequest(&w, "number is not a valid integer "+commands[1])
				return
			}
			resp, err = service.SaveBet(user, number)
		} else if strings.EqualFold("end", firstCommand) {
			resp, err = service.EndBet(user)
		} else if strings.EqualFold("info", firstCommand) {
			betID := -1
			if len(commands) > 1 {
				betID, err = strconv.Atoi(commands[1])
				if err != nil {
					writeResponseWithBadRequest(&w, "id is not a valid integer "+commands[1])
					return
				}
			}

			resp, err = service.GetBetInfo(betID)
		} else if strings.EqualFold("whowins", firstCommand) {
			if len(commands) > 1 {
				referenceNumber, err := strconv.Atoi(commands[1])
				if err != nil {
					writeResponseWithBadRequest(&w, "reference number is not a valid integer "+commands[1])
					return
				}
				resp, err = service.CalculateWhoWins(referenceNumber)
			} else {
				writeResponseWithBadRequest(&w, "usage: /bet whowins <number>")
				return
			}
		} else if strings.EqualFold("savefor", firstCommand) && user == "tarik" {
			if len(commands) != 3 {
				writeResponseWithBadRequest(&w, availableCommands)
				return
			}
			user = commands[1]
			number, err := strconv.Atoi(commands[2])
			if err != nil {
				writeResponseWithBadRequest(&w, "number is not a valid integer "+commands[2])
				return
			}
			resp, err = service.SaveBet(user, number)
		} else if strings.EqualFold("listabsent", firstCommand) {
			resp, err = service.ListAbsentUsers()
			if err != nil {
			}
		} else if strings.EqualFold("savewinner", firstCommand) && user == "tarik" {

			if len(commands) != 3 {
				writeResponseWithBadRequest(&w, availableCommands)
			}
			winner, err := strconv.Atoi(commands[2])
			if err != nil {
				writeResponseWithBadRequest(&w, "number is not a valid integer "+commands[2])
				return
			}

			betID, err := strconv.Atoi(commands[1])
			if err != nil {
				writeResponseWithBadRequest(&w, "number is not a valid integer "+commands[1])
				return
			}
			resp, err = service.SaveWinner(betID, winner)
		}

		if err != nil {
			writeResponseWithBadRequest(&w, err.Error())
			return
		}
		fmt.Fprint(w, resp)
	}
}

func writeResponseWithBadRequest(w *http.ResponseWriter, text string) {
	(*w).WriteHeader(http.StatusBadRequest)
	fmt.Fprintf(*w, text)
}
func parseConf(confFileName string) (*slackbet.Conf, error) {
	file, err := os.Open(confFileName)
	if err != nil {
		return nil, err
	}
	c := &slackbet.Conf{}
	err = json.NewDecoder(file).Decode(c)
	if err != nil {
		return nil, err
	}
	return c, nil
}
func main() {
	conf, err := parseConf("conf.json")
	if err != nil {
		fmt.Println("conf cannot be read", err)
		return
	}
	slackService := &slack.SlackService{}
	service := &bet.BetService{Repo: &repo.RedisRepo{Url: conf.RedisUrl}, Conf: conf, SlackService: slackService}
	http.HandleFunc("/bet", betHandler(service))
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "Hello mate.")
	})
	http.ListenAndServe(":"+conf.Port, nil)
}
<commit_msg>Fix slack service initialization<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"os"
	"strconv"
	"strings"

	"github.com/mtyurt/slackbet"
	"github.com/mtyurt/slackbet/bet"
	"github.com/mtyurt/slackbet/repo"
	"github.com/mtyurt/slackbet/slack"
)

const availableCommands = "Available commands: save, start, end, list, info, whowins"

func betHandler(service slackbet.BetService) func(w http.ResponseWriter, r *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		err := service.ParseRequestAndCheckToken(r)
		if err != nil {
			writeResponseWithBadRequest(&w, err.Error())
			return
		}
		user := r.FormValue("user_name")
		text := r.FormValue("text")
		commands := strings.Split(text, " ")
		if commands == nil || len(commands) < 1 {
			writeResponseWithBadRequest(&w, availableCommands)
			return
		}
		firstCommand := commands[0]
		if !strings.EqualFold("save", firstCommand) &&
			!strings.EqualFold("start", firstCommand) &&
			!strings.EqualFold("end", firstCommand) &&
			!strings.EqualFold("whowins", firstCommand) &&
			!strings.EqualFold("savefor", firstCommand) &&
			!strings.EqualFold("listabsent", firstCommand) &&
			!strings.EqualFold("savewinner", firstCommand) &&
			!strings.EqualFold("list", firstCommand) &&
			!strings.EqualFold("info", firstCommand) {
			writeResponseWithBadRequest(&w, availableCommands)
			return
		}
		var resp string
		if strings.EqualFold("start", firstCommand) {
			resp, err = service.StartNewBet(user)
		} else if strings.EqualFold("list", firstCommand) {
			resp, err = service.ListBets()
		} else if strings.EqualFold("save", firstCommand) {
			if len(commands) != 2 {
				writeResponseWithBadRequest(&w, "save command format: save <number>")
				return
			}
			number, err := strconv.Atoi(commands[1])
			if err != nil {
				writeResponseWithBadRequest(&w, "number is not a valid integer "+commands[1])
				return
			}
			resp, err = service.SaveBet(user, number)
		} else if strings.EqualFold("end", firstCommand) {
			resp, err = service.EndBet(user)
		} else if strings.EqualFold("info", firstCommand) {
			betID := -1
			if len(commands) > 1 {
				betID, err = strconv.Atoi(commands[1])
				if err != nil {
					writeResponseWithBadRequest(&w, "id is not a valid integer "+commands[1])
					return
				}
			}

			resp, err = service.GetBetInfo(betID)
		} else if strings.EqualFold("whowins", firstCommand) {
			if len(commands) > 1 {
				referenceNumber, err := strconv.Atoi(commands[1])
				if err != nil {
					writeResponseWithBadRequest(&w, "reference number is not a valid integer "+commands[1])
					return
				}
				resp, err = service.CalculateWhoWins(referenceNumber)
			} else {
				writeResponseWithBadRequest(&w, "usage: /bet whowins <number>")
				return
			}
		} else if strings.EqualFold("savefor", firstCommand) && user == "tarik" {
			if len(commands) != 3 {
				writeResponseWithBadRequest(&w, availableCommands)
				return
			}
			user = commands[1]
			number, err := strconv.Atoi(commands[2])
			if err != nil {
				writeResponseWithBadRequest(&w, "number is not a valid integer "+commands[2])
				return
			}
			resp, err = service.SaveBet(user, number)
		} else if strings.EqualFold("listabsent", firstCommand) {
			resp, err = service.ListAbsentUsers()
			if err != nil {
			}
		} else if strings.EqualFold("savewinner", firstCommand) && user == "tarik" {

			if len(commands) != 3 {
				writeResponseWithBadRequest(&w, availableCommands)
			}
			winner, err := strconv.Atoi(commands[2])
			if err != nil {
				writeResponseWithBadRequest(&w, "number is not a valid integer "+commands[2])
				return
			}

			betID, err := strconv.Atoi(commands[1])
			if err != nil {
				writeResponseWithBadRequest(&w, "number is not a valid integer "+commands[1])
				return
			}
			resp, err = service.SaveWinner(betID, winner)
		}

		if err != nil {
			writeResponseWithBadRequest(&w, err.Error())
			return
		}
		fmt.Fprint(w, resp)
	}
}

func writeResponseWithBadRequest(w *http.ResponseWriter, text string) {
	(*w).WriteHeader(http.StatusBadRequest)
	fmt.Fprintf(*w, text)
}
func parseConf(confFileName string) (*slackbet.Conf, error) {
	file, err := os.Open(confFileName)
	if err != nil {
		return nil, err
	}
	c := &slackbet.Conf{}
	err = json.NewDecoder(file).Decode(c)
	if err != nil {
		return nil, err
	}
	return c, nil
}
func main() {
	conf, err := parseConf("conf.json")
	if err != nil {
		fmt.Println("conf cannot be read", err)
		return
	}
	slackService := &slack.SlackService{Conf: conf}
	service := &bet.BetService{Repo: &repo.RedisRepo{Url: conf.RedisUrl}, Conf: conf, SlackService: slackService}
	http.HandleFunc("/bet", betHandler(service))
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "Hello mate.")
	})
	http.ListenAndServe(":"+conf.Port, nil)
}
<|endoftext|>


<commit_before>package funcgo.clojure_cookbook_test
import(
        test midje.sweet
        fgo funcgo.core
        string clojure.string
)

func add(x,y) {
        x + y
}
test.fact("Simple example",
        add(1,2),
        =>, 3
)

test.fact("More complex example",
        into([],  \range(1, 20)),
        =>,  [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19]
)

test.fact("Any function of two arguments can be written infix",
        [] into \range(1, 20),
        =>,  [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19]
)

test.fact("Infix is most convenient for math operators.",
        1 + 2,
        =>, 3
)

test.fact("Dotted identifers are from other packages.",
        // import section includes
        //    string clojure.string
        string.isBlank(""),
        =>, true
)

test.fact("Capitalize first character in a string.",
        string.capitalize("this is a proper sentence."),
        =>,  "This is a proper sentence."
)

test.fact("Capitalize or lower-case all characters.",

        string.upperCase("loud noises!"),
        =>, "LOUD NOISES!",

        string.lowerCase("COLUMN_HEADER_ONE"),
        =>, "column_header_one",

        string.lowerCase("!&$#@#%^[]"),
        =>, "!&$#@#%^[]",

        string.upperCase("Dépêchez-vous, l'ordinateur!"),
        =>, "DÉPÊCHEZ-VOUS, L'ORDINATEUR!"
)

test.fact("Remove whitespace at beginning and end.",
        string.trim(" \tBacon ipsum dolor sit.\n"),
        =>, "Bacon ipsum dolor sit."
)

test.fact("Collapse whitespace into single whitespace",
        string.replace("Who\t\nput  all this\fwhitespace here?", /\s+/, " "),
        =>, "Who put all this whitespace here?"
)

test.fact("Windows to Unix line-endings",
        string.replace("Line 1\r\nLine 2", "\r\n", "\n"),
        =>, "Line 1\nLine 2"
)

test.fact("Trim only from one end",
        string.triml(" Column Header\t"),
        =>, "Column Header\t",

        string.trimr("\t\t* Second-level bullet.\n"),
        =>, "\t\t* Second-level bullet."
)

test.fact("Concatenate strings",
        str("John", " ", "Doe"),
        =>, "John Doe"
)

test.fact("Can concatenate consts.",
        const(
                firstName = "John"
                lastName = "Doe"
                age = 42
        )
        str(lastName, ", ", firstName, " - age: ", age),
        =>, "Doe, John - age: 42"
)

firstName := "John"
lastName := "Doe"
age := 42
test.fact("Can concatenate vars.",
        str(lastName, ", ", firstName, " - age: ", age),
        =>, "Doe, John - age: 42"
)

test.fact("turn characters into a string",
        apply(str, "ROT13: ", ['W', 'h', 'y', 'v', 'h', 'f', ' ', 'P', 'n', 'r', 'f', 'n', 'e']),
        =>, "ROT13: Whyvhf Pnrfne"
)

lines := [
        "#! /bin/bash\n",
        "du -a ./ | sort -n -r\n"
]
test.fact("make file from lines (with newlines)",
        str apply lines,
        =>,  "#! /bin/bash\ndu -a ./ | sort -n -r\n"
)

header := "first_name,last_name,employee_number\n"
rows := [
        "luke,vanderhart,1",
        "ryan,neufeld,2"
]
test.fact("Making CSV from header vector of rows",
        apply(str, header, ("\n" interpose rows)),
        =>, `first_name,last_name,employee_number
luke,vanderhart,1
ryan,neufeld,2`
)

foodItems := ["milk", "butter", "flour", "eggs"]
test.fact("Join can be easier",

        string.join(", ", foodItems),
        =>, "milk, butter, flour, eggs",

        ", " string.join foodItems,
        =>, "milk, butter, flour, eggs",

        string.join( [1, 2, 3, 4] ),
        =>, "1234"
)

test.fact("seq() exposes the characters in a string",
        seq("Hello, world!"),
        =>, ['H', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!']
)

func isYelling(s) {
  isEvery(
          func(ch) { !Character::isLetter(ch) || Character::isUpperCase(ch) },
          s
  )
}

test.fact("Function taking a sequence will cooerce a string into a set of chars",

        // count of chars in string
        frequencies(string.lowerCase("An adult all about A's")),
        =>, {' ':4, 'a':5, 'b':1, 'd':1, '\'':1, 'l':3, 'n':1, 'o':1, 's':1, 't':2, 'u':2},

        //  every letter capitalized?
        isYelling("LOUD NOISES!"),
        =>, true,

        isYelling("Take a DEEP breath."),
        =>, false
)

test.fact("Can transform characters back into a string",
        str apply ['H', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!'],
        =>, "Hello, world!"
)

test.fact("int function converts characters to integers",

        int('a'),
        =>, 97,

        int('ø'),
        =>, 248,

        int('α'),  // Greek letter alpha
        =>, 945,

        int('\u03B1'), // Greek letter alpha (by code point)
        =>, 945,

        int map "Hello, world!",
        =>, [72, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 33]
)

test.fact("char function does the opposite",
        char(97),
        =>, 'a',

        char(125),
        =>,  '}',

        char(945),
        =>,  'α',

        reduce(
                func(acc, i){str(acc, char(i))},
                "",
                [115, 101, 99, 114, 101, 116, 32, 109, 101, 115, 115, 97, 103, 101, 115]
        ),
        =>,  "secret messages"
)

me := {FIRST_NAME: "Ryan", FAVORITE_LANGUAGE: "Clojure"}

test.fact("str is the easiest way of formatting values into a string",

        str("My name is ", me[FIRST_NAME],
                ", and I really like to program in ", me[FAVORITE_LANGUAGE]),
        =>, "My name is Ryan, and I really like to program in Clojure",

        str apply (" " interpose [1, 2.000, 3/1, 4/9]),
        =>, "1 2.0 3 4/9"
)

// Produce a filename with a zero-padded sortable index
func filename(name, i) {
        format("%03d-%s", i, name)
}

test.fact("format is another way of constructing strings",
        filename("my-awesome-file.txt", 42),
        =>, "042-my-awesome-file.txt"
)

// Create a table using justification
func tableify(row) {
        apply(format, "%-20s | %-20s | %-20s", row)
}

header := ["First Name", "Last Name", "Employee ID"]
employees := [
        ["Ryan", "Neufeld", 2],
        ["Luke", "Vanderhart", 1]
]

mapv(
	println,
        map(
		tableify,
                concat([header], employees)
	)
)
// *out*
// First Name           | Last Name            | Employee ID
// Ryan                 | Neufeld              | 2
// Luke                 | Vanderhart           | 1

->>(
        concat([header], employees),
        map(tableify),
        mapv(println)
)
// *out*
// First Name           | Last Name            | Employee ID
// Ryan                 | Neufeld              | 2
// Luke                 | Vanderhart           | 1
<commit_msg>add more cookbook examples<commit_after>package funcgo.clojure_cookbook_test
import(
        test midje.sweet
        fgo funcgo.core
        string clojure.string
)

func add(x,y) {
        x + y
}
test.fact("Simple example",
        add(1,2),
        =>, 3
)

test.fact("More complex example",
        into([],  \range(1, 20)),
        =>,  [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19]
)

test.fact("Any function of two arguments can be written infix",
        [] into \range(1, 20),
        =>,  [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19]
)

test.fact("Infix is most convenient for math operators.",
        1 + 2,
        =>, 3
)

test.fact("Dotted identifers are from other packages.",
        // import section includes
        //    string clojure.string
        string.isBlank(""),
        =>, true
)

test.fact("Capitalize first character in a string.",
        string.capitalize("this is a proper sentence."),
        =>,  "This is a proper sentence."
)

test.fact("Capitalize or lower-case all characters.",

        string.upperCase("loud noises!"),
        =>, "LOUD NOISES!",

        string.lowerCase("COLUMN_HEADER_ONE"),
        =>, "column_header_one",

        string.lowerCase("!&$#@#%^[]"),
        =>, "!&$#@#%^[]",

        string.upperCase("Dépêchez-vous, l'ordinateur!"),
        =>, "DÉPÊCHEZ-VOUS, L'ORDINATEUR!"
)

test.fact("Remove whitespace at beginning and end.",
        string.trim(" \tBacon ipsum dolor sit.\n"),
        =>, "Bacon ipsum dolor sit."
)

test.fact("Collapse whitespace into single whitespace",
        string.replace("Who\t\nput  all this\fwhitespace here?", /\s+/, " "),
        =>, "Who put all this whitespace here?"
)

test.fact("Windows to Unix line-endings",
        string.replace("Line 1\r\nLine 2", "\r\n", "\n"),
        =>, "Line 1\nLine 2"
)

test.fact("Trim only from one end",
        string.triml(" Column Header\t"),
        =>, "Column Header\t",

        string.trimr("\t\t* Second-level bullet.\n"),
        =>, "\t\t* Second-level bullet."
)

test.fact("Concatenate strings",
        str("John", " ", "Doe"),
        =>, "John Doe"
)

test.fact("Can concatenate consts.",
        const(
                firstName = "John"
                lastName = "Doe"
                age = 42
        )
        str(lastName, ", ", firstName, " - age: ", age),
        =>, "Doe, John - age: 42"
)

firstName := "John"
lastName := "Doe"
age := 42
test.fact("Can concatenate vars.",
        str(lastName, ", ", firstName, " - age: ", age),
        =>, "Doe, John - age: 42"
)

test.fact("turn characters into a string",
        apply(str, "ROT13: ", ['W', 'h', 'y', 'v', 'h', 'f', ' ', 'P', 'n', 'r', 'f', 'n', 'e']),
        =>, "ROT13: Whyvhf Pnrfne"
)

lines := [
        "#! /bin/bash\n",
        "du -a ./ | sort -n -r\n"
]
test.fact("make file from lines (with newlines)",
        str apply lines,
        =>,  "#! /bin/bash\ndu -a ./ | sort -n -r\n"
)

header := "first_name,last_name,employee_number\n"
rows := [
        "luke,vanderhart,1",
        "ryan,neufeld,2"
]
test.fact("Making CSV from header vector of rows",
        apply(str, header, ("\n" interpose rows)),
        =>, `first_name,last_name,employee_number
luke,vanderhart,1
ryan,neufeld,2`
)

foodItems := ["milk", "butter", "flour", "eggs"]
test.fact("Join can be easier",

        string.join(", ", foodItems),
        =>, "milk, butter, flour, eggs",

        ", " string.join foodItems,
        =>, "milk, butter, flour, eggs",

        string.join( [1, 2, 3, 4] ),
        =>, "1234"
)

test.fact("seq() exposes the characters in a string",
        seq("Hello, world!"),
        =>, ['H', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!']
)

func isYelling(s) {
  isEvery(
          func(ch) { !Character::isLetter(ch) || Character::isUpperCase(ch) },
          s
  )
}

test.fact("Function taking a sequence will cooerce a string into a set of chars",

        // count of chars in string
        frequencies(string.lowerCase("An adult all about A's")),
        =>, {' ':4, 'a':5, 'b':1, 'd':1, '\'':1, 'l':3, 'n':1, 'o':1, 's':1, 't':2, 'u':2},

        //  every letter capitalized?
        isYelling("LOUD NOISES!"),
        =>, true,

        isYelling("Take a DEEP breath."),
        =>, false
)

test.fact("Can transform characters back into a string",
        str apply ['H', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!'],
        =>, "Hello, world!"
)

test.fact("int function converts characters to integers",

        int('a'),
        =>, 97,

        int('ø'),
        =>, 248,

        int('α'),  // Greek letter alpha
        =>, 945,

        int('\u03B1'), // Greek letter alpha (by code point)
        =>, 945,

        int map "Hello, world!",
        =>, [72, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 33]
)

test.fact("char function does the opposite",
        char(97),
        =>, 'a',

        char(125),
        =>,  '}',

        char(945),
        =>,  'α',

        reduce(
                func(acc, i){str(acc, char(i))},
                "",
                [115, 101, 99, 114, 101, 116, 32, 109, 101, 115, 115, 97, 103, 101, 115]
        ),
        =>,  "secret messages"
)

me := {FIRST_NAME: "Ryan", FAVORITE_LANGUAGE: "Clojure"}

test.fact("str is the easiest way of formatting values into a string",

        str("My name is ", me[FIRST_NAME],
                ", and I really like to program in ", me[FAVORITE_LANGUAGE]),
        =>, "My name is Ryan, and I really like to program in Clojure",

        str apply (" " interpose [1, 2.000, 3/1, 4/9]),
        =>, "1 2.0 3 4/9"
)

// Produce a filename with a zero-padded sortable index
func filename(name, i) {
        format("%03d-%s", i, name)
}

test.fact("format is another way of constructing strings",
        filename("my-awesome-file.txt", 42),
        =>, "042-my-awesome-file.txt",
	"%07.3f" format 0.005,
	=>, "000.005"
)

// Create a table using justification
func tableify(row) {
        apply(format, "%-20s | %-20s | %-20s", row)
}

header := ["First Name", "Last Name", "Employee ID"]
employees := [
        ["Ryan", "Neufeld", 2],
        ["Luke", "Vanderhart", 1]
]

mapv(
	println,
        map(
		tableify,
                concat([header], employees)
	)
)
// *out*
// First Name           | Last Name            | Employee ID
// Ryan                 | Neufeld              | 2
// Luke                 | Vanderhart           | 1

->>(
        concat([header], employees),
        map(tableify),
        mapv(println)
)
// *out*
// First Name           | Last Name            | Employee ID
// Ryan                 | Neufeld              | 2
// Luke                 | Vanderhart           | 1

test.fact("Regular expressions, using reFind",

	/\d+/ reFind "I've just finished reading Fahrenheit 451",
	=>, "451",

	/Bees/ reFind "Beads aren't cheap.",
	=>, nil
)

test.fact("To match only the whole string use reMatches",

	/\w+/ reFind "my-param",
	=>, "my",

	/\w+/ reMatches "my-param",
	=>, nil,

	/\w+/ reMatches "justLetters",
	=>, "justLetters"
)

// Extract Twitter identifiers in a tweet
func mentions(tweet) {
  /(@|#)(\w+)/ reSeq tweet
}

// Capture and decompose a phone number and its title
rePhoneNumber := /(\w+): \((\d{3})\) (\d{3}-\d{4})/

test.fact("Extract strings from a larger string using reSeq",

        /\w+/ reSeq "My Favorite Things",
	=>, ["My", "Favorite", "Things"],

	/\d{3}-\d{4}/ reSeq "My phone number is 555-1234.",
	=>, ["555-1234"],

        mentions("So long, @earth, and thanks for all the #fish. #goodbyes"),
        =>, [["@earth", "@", "earth"], ["#fish", "#", "fish"], ["#goodbyes", "#", "goodbyes"]],

        rePhoneNumber reSeq "Home: (919) 555-1234, Work: (191) 555-1234",
        =>, [["Home: (919) 555-1234", "Home", "919", "555-1234"],
	["Work: (191) 555-1234", "Work", "191", "555-1234"]]

)
<|endoftext|>


<commit_before>package main

import (
	"crypto/rsa"

	cryptorand "crypto/rand"
	"crypto/tls"
	"crypto/x509"
	"crypto/x509/pkix"
	"errors"
	"github.com/Sirupsen/logrus"
	"github.com/hlandau/acme/acmeapi"
	"github.com/hlandau/acme/acmeapi/acmeutils"
	context "golang.org/x/net/context"
	"net"
	"strings"
	"sync"
	"time"
	"fmt"
	"encoding/pem"
)

const (
	SNI01_EXPIRE_TOKEN time.Duration = time.Minute * 10
)

type acmeStruct struct {
	serverAddress string
	privateKey    *rsa.PrivateKey
	client        *acmeapi.Client

	mutex                *sync.Mutex
	acmeauthDomainsMutex *sync.Mutex
	acmeAuthDomains      map[string]time.Time
}

func (this *acmeStruct) Init() {
	this.client = &acmeapi.Client{
		AccountKey:   this.privateKey,
		DirectoryURL: this.serverAddress,
	}

	this.mutex = &sync.Mutex{}

	this.acmeauthDomainsMutex = &sync.Mutex{}
	this.acmeAuthDomains = make(map[string]time.Time)
	this.CleanupTimer()
}

func (this *acmeStruct) RegisterEnsure(ctx context.Context) (err error) {
	reg := &acmeapi.Registration{}
	for i := 0; i < TRY_COUNT+1; i++ { // +1 count need for request latest agreement uri
		reg.AgreementURI = reg.LatestAgreementURI
		if reg.AgreementURI != "" {
			logrus.Info("Auto agree with terms:", reg.LatestAgreementURI)
		}
		err = this.client.UpsertRegistration(reg, ctx)
		if reg.AgreementURI != "" && err == nil {
			return
		}
	}
	return err
}

func (this *acmeStruct) Cleanup() {
	this.mutex.Lock()
	defer this.mutex.Unlock()

	now := time.Now()
	for token, expire := range this.acmeAuthDomains {
		if expire.Before(now) {
			delete(this.acmeAuthDomains, token)
		}
	}
}

func (this *acmeStruct) CleanupTimer() {
	this.Cleanup()
	time.AfterFunc(SNI01_EXPIRE_TOKEN, this.Cleanup)
}

func (this *acmeStruct) CreateCertificate(domain string) (cert *tls.Certificate, err error) {
	// Check suffix for avoid mutex sync in DeleteAcmeAuthDomain
	if strings.HasSuffix(domain, ".acme.invalid") {
		logrus.Debugf("Detect auth-domain mode for domain '%v'", domain)
		if this.DeleteAcmeAuthDomain(domain) {
			logrus.Debugf("Return self-signed certificate for domain '%v'", domain)
			return this.createCertificateSelfSigned(domain)
		} else {
			logrus.Debugf("Detect auth-domain is not present in list '%v'", domain)
			return nil, errors.New("Now allowed auth-domain")
		}
	}

	// check about we serve the domain
	ips, err := net.LookupIP(domain)
	if err != nil {
		logrus.Warnf("Can't lookup ip for domain '%v': %v", domain, err)
		return nil, errors.New("Can't lookup ip of the domain")
	}
	isLocalIP := false
checkLocalIP:
	for _, ip := range ips {
		for _, localIP := range localIPs {
			if ip.Equal(localIP) {
				isLocalIP = true
				break checkLocalIP
			}
		}
	}
	if !isLocalIP {
		logrus.Warnf("Domain have ip of other server. Domain '%v', Domain ips: %v, Server ips: %v", domain, ips, localIPs)
		return nil, errors.New("Domain have ip of other server.")
	}

	return this.createCertificateAcme(domain)
}

func (this *acmeStruct) createCertificateAcme(domain string) (cert *tls.Certificate, err error) {
	this.mutex.Lock()
	defer this.mutex.Unlock()

	var auth *acmeapi.Authorization

	ctx, cancelFunc := context.WithTimeout(context.Background(), LETSENCRYPT_CREATE_CERTIFICATE_TIMEOUT)
	defer cancelFunc()

	for i := 0; i < TRY_COUNT; i++ {
		auth, err = this.client.NewAuthorization(domain, ctx)
		if err == nil {
			break
		} else {
			logrus.Infof("Can't create new authorization for domain '%v': %v", domain, err)
			time.Sleep(RETRY_SLEEP)
		}
	}
	if err == nil {
		logrus.Infof("Create authorization for domain '%v'", domain)
	} else {
		logrus.Errorf("Can't create new authorization for domain '%v': %v", domain, err)
		return nil, errors.New("Can't create new authorization for domain")
	}

	if logrus.GetLevel() >= logrus.DebugLevel {
		challengeTypes := make([]string, len(auth.Challenges))
		for i := range auth.Challenges {
			challengeTypes[i] = auth.Challenges[i].Type
		}
		logrus.Debugf("Challenge types for domain '%v': %v. Challenge combinations: %v", domain, challengeTypes, auth.Combinations)
	}

	canAuthorize := false
	var challenge *acmeapi.Challenge
	for _, cmb := range auth.Combinations {
		if len(cmb) == 1 && auth.Challenges[cmb[0]].Type == "tls-sni-01" {
			canAuthorize = true
			challenge = auth.Challenges[cmb[0]]
			break
		}
	}
	if !canAuthorize {
		logrus.Errorf("Can't find good challange combination for domain: '%v'", domain)
		return nil, errors.New("Can't find good challange combination")
	}

	acmeHostName, err := acmeutils.TLSSNIHostname(this.privateKey, challenge.Token)
	if err == nil {
		logrus.Debugf("Create acme-auth hostname for domain '%v': %v", domain, acmeHostName)
	} else {
		logrus.Errorf("Can't create acme domain for domain '%v' token '%v': %v", domain, challenge.Token, err)
		return nil, errors.New("Can't create acme domain")
	}
	this.PutAcmeAuthDomain(acmeHostName)

	challengeResponse, err := acmeutils.ChallengeResponseJSON(this.privateKey, challenge.Token, challenge.Type)
	if err == nil {
		logrus.Debugf("Create challenge response for domain '%v'", domain)
	} else {
		logrus.Errorf("Can't create challenge response for domain '%v', token '%v', challenge type %v: %v",
			domain, challenge.Token, challenge.Type, err)
		return nil, errors.New("Can't create challenge response")
	}
	for i := 0; i < TRY_COUNT; i++ {
		err = this.client.RespondToChallenge(challenge, challengeResponse, this.privateKey, ctx)
		if err == nil {
			break
		} else {
			logrus.Info("Can't send response for challenge of domain '%v': %v", domain, err)
			time.Sleep(RETRY_SLEEP)
		}
	}
	if err == nil {
		logrus.Debugf("Send challenge response for domain '%v'", domain)
	} else {
		logrus.Errorf("Can't send response for challenge of domain '%v': %v", domain, err)
		return nil, errors.New("Can't send response for challenge")
	}

	for i := 0; i < TRY_COUNT; i++ {
		err = this.client.LoadChallenge(challenge, ctx)
		if err != nil {
			logrus.Infof("Can't load challenge for domain '%v': %v", domain, err)
			time.Sleep(RETRY_SLEEP)
		}
	}
	if err == nil {
		logrus.Debugf("Load challenge for domain '%v'", domain)
	} else {
		logrus.Errorf("Can't load challenge for domain '%v': %v", domain, err)
		return nil, errors.New("Can't load challenge")
	}

	// Generate CSR
	certKey, err := rsa.GenerateKey(cryptorand.Reader, PRIVATE_KEY_BITS)
	if err == nil {
		logrus.Debugf("Create private key for domain '%v'", domain)
	} else {
		logrus.Errorf("Can't create rsa key for domain '%v': %v", err)
		return nil, errors.New("Can't create rsa key")
	}
	certRequest := &x509.CertificateRequest{
		SignatureAlgorithm: x509.SHA256WithRSA,
		PublicKeyAlgorithm: x509.RSA,
		PublicKey:          &certKey.PublicKey,
		Subject:            pkix.Name{CommonName: domain},
		DNSNames:           []string{domain},
	}
	csrDER, err := x509.CreateCertificateRequest(cryptorand.Reader, certRequest, certKey)
	if err == nil {
		logrus.Debugf("Create CSR for domain '%v'", domain)
	} else {
		logrus.Errorf("Can't create csr for domain '%v': %v", domain, err)
		return nil, errors.New("Can't create csr")
	}

	var certResponse *acmeapi.Certificate
	for i := 0; i < TRY_COUNT; i++ {
		certResponse, err = this.client.RequestCertificate(csrDER, ctx)
		if err != nil {
			logrus.Infof("Can't get certificate for domain '%v': %v", domain, err)
			time.Sleep(RETRY_SLEEP)
		}
	}
	if err == nil {
		logrus.Infof("Get certificate for domain '%v'", domain)
	} else {
		logrus.Errorf("Can't get certificate for domain '%v': %v", domain, err)
		return nil, errors.New("Can't request certificate")
	}
	cert = &tls.Certificate{}
	cert.Certificate = [][]byte{certResponse.Certificate}
	fmt.Printf("CERT:\n%s\n", certResponse.Certificate)
	cert.Leaf, err = x509.ParseCertificate(certResponse.Certificate)
	if err == nil {
		logrus.Debugf("Parse certificate for domain '%v'", domain)
	} else {
		logrus.Errorf("Can't parse certificate for domain '%v':%v", domain, err)
		return nil, errors.New("Can't parse certificate")
	}
	fmt.Printf("CERT PARSED\n%#v\n", cert)

	certPEM := pem.EncodeToMemory(&pem.Block{Bytes: certResponse.Certificate, Type: "CERTIFICATE"})
	fmt.Printf("CERT PEM:\n%s\n", certPEM)

	return cert, nil
}

func (this *acmeStruct) createCertificateSelfSigned(domain string) (cert *tls.Certificate, err error) {
	derCert, privateKey, err := acmeutils.CreateTLSSNICertificate(domain)
	if err != nil {
		logrus.Errorf("Can't create tls-sni-01 self-signed certificate for '%v': %v", domain, err)
		return nil, err
	}

	cert = &tls.Certificate{}
	cert.Certificate = [][]byte{derCert}
	cert.PrivateKey = privateKey
	return cert, nil
}

func (this *acmeStruct) PutAcmeAuthDomain(domain string) {
	this.acmeauthDomainsMutex.Lock()
	defer this.acmeauthDomainsMutex.Unlock()

	logrus.Debug("Put acme auth domain:", domain)
	this.acmeAuthDomains[domain] = time.Now().Add(SNI01_EXPIRE_TOKEN)
}

func (this *acmeStruct) DeleteAcmeAuthDomain(domain string) bool {
	this.acmeauthDomainsMutex.Lock()
	defer this.acmeauthDomainsMutex.Unlock()

	logrus.Debug("Delete acme auth domain:", domain)
	_, ok := this.acmeAuthDomains[domain]
	if ok {
		delete(this.acmeAuthDomains, domain)
	}
	return ok
}
<commit_msg>parse pem<commit_after>package main

import (
	"crypto/rsa"

	cryptorand "crypto/rand"
	"crypto/tls"
	"crypto/x509"
	"crypto/x509/pkix"
	"errors"
	"github.com/Sirupsen/logrus"
	"github.com/hlandau/acme/acmeapi"
	"github.com/hlandau/acme/acmeapi/acmeutils"
	context "golang.org/x/net/context"
	"net"
	"strings"
	"sync"
	"time"
	"fmt"
	"encoding/pem"
)

const (
	SNI01_EXPIRE_TOKEN time.Duration = time.Minute * 10
)

type acmeStruct struct {
	serverAddress string
	privateKey    *rsa.PrivateKey
	client        *acmeapi.Client

	mutex                *sync.Mutex
	acmeauthDomainsMutex *sync.Mutex
	acmeAuthDomains      map[string]time.Time
}

func (this *acmeStruct) Init() {
	this.client = &acmeapi.Client{
		AccountKey:   this.privateKey,
		DirectoryURL: this.serverAddress,
	}

	this.mutex = &sync.Mutex{}

	this.acmeauthDomainsMutex = &sync.Mutex{}
	this.acmeAuthDomains = make(map[string]time.Time)
	this.CleanupTimer()
}

func (this *acmeStruct) RegisterEnsure(ctx context.Context) (err error) {
	reg := &acmeapi.Registration{}
	for i := 0; i < TRY_COUNT+1; i++ { // +1 count need for request latest agreement uri
		reg.AgreementURI = reg.LatestAgreementURI
		if reg.AgreementURI != "" {
			logrus.Info("Auto agree with terms:", reg.LatestAgreementURI)
		}
		err = this.client.UpsertRegistration(reg, ctx)
		if reg.AgreementURI != "" && err == nil {
			return
		}
	}
	return err
}

func (this *acmeStruct) Cleanup() {
	this.mutex.Lock()
	defer this.mutex.Unlock()

	now := time.Now()
	for token, expire := range this.acmeAuthDomains {
		if expire.Before(now) {
			delete(this.acmeAuthDomains, token)
		}
	}
}

func (this *acmeStruct) CleanupTimer() {
	this.Cleanup()
	time.AfterFunc(SNI01_EXPIRE_TOKEN, this.Cleanup)
}

func (this *acmeStruct) CreateCertificate(domain string) (cert *tls.Certificate, err error) {
	// Check suffix for avoid mutex sync in DeleteAcmeAuthDomain
	if strings.HasSuffix(domain, ".acme.invalid") {
		logrus.Debugf("Detect auth-domain mode for domain '%v'", domain)
		if this.DeleteAcmeAuthDomain(domain) {
			logrus.Debugf("Return self-signed certificate for domain '%v'", domain)
			return this.createCertificateSelfSigned(domain)
		} else {
			logrus.Debugf("Detect auth-domain is not present in list '%v'", domain)
			return nil, errors.New("Now allowed auth-domain")
		}
	}

	// check about we serve the domain
	ips, err := net.LookupIP(domain)
	if err != nil {
		logrus.Warnf("Can't lookup ip for domain '%v': %v", domain, err)
		return nil, errors.New("Can't lookup ip of the domain")
	}
	isLocalIP := false
checkLocalIP:
	for _, ip := range ips {
		for _, localIP := range localIPs {
			if ip.Equal(localIP) {
				isLocalIP = true
				break checkLocalIP
			}
		}
	}
	if !isLocalIP {
		logrus.Warnf("Domain have ip of other server. Domain '%v', Domain ips: %v, Server ips: %v", domain, ips, localIPs)
		return nil, errors.New("Domain have ip of other server.")
	}

	return this.createCertificateAcme(domain)
}

func (this *acmeStruct) createCertificateAcme(domain string) (cert *tls.Certificate, err error) {
	this.mutex.Lock()
	defer this.mutex.Unlock()

	var auth *acmeapi.Authorization

	ctx, cancelFunc := context.WithTimeout(context.Background(), LETSENCRYPT_CREATE_CERTIFICATE_TIMEOUT)
	defer cancelFunc()

	for i := 0; i < TRY_COUNT; i++ {
		auth, err = this.client.NewAuthorization(domain, ctx)
		if err == nil {
			break
		} else {
			logrus.Infof("Can't create new authorization for domain '%v': %v", domain, err)
			time.Sleep(RETRY_SLEEP)
		}
	}
	if err == nil {
		logrus.Infof("Create authorization for domain '%v'", domain)
	} else {
		logrus.Errorf("Can't create new authorization for domain '%v': %v", domain, err)
		return nil, errors.New("Can't create new authorization for domain")
	}

	if logrus.GetLevel() >= logrus.DebugLevel {
		challengeTypes := make([]string, len(auth.Challenges))
		for i := range auth.Challenges {
			challengeTypes[i] = auth.Challenges[i].Type
		}
		logrus.Debugf("Challenge types for domain '%v': %v. Challenge combinations: %v", domain, challengeTypes, auth.Combinations)
	}

	canAuthorize := false
	var challenge *acmeapi.Challenge
	for _, cmb := range auth.Combinations {
		if len(cmb) == 1 && auth.Challenges[cmb[0]].Type == "tls-sni-01" {
			canAuthorize = true
			challenge = auth.Challenges[cmb[0]]
			break
		}
	}
	if !canAuthorize {
		logrus.Errorf("Can't find good challange combination for domain: '%v'", domain)
		return nil, errors.New("Can't find good challange combination")
	}

	acmeHostName, err := acmeutils.TLSSNIHostname(this.privateKey, challenge.Token)
	if err == nil {
		logrus.Debugf("Create acme-auth hostname for domain '%v': %v", domain, acmeHostName)
	} else {
		logrus.Errorf("Can't create acme domain for domain '%v' token '%v': %v", domain, challenge.Token, err)
		return nil, errors.New("Can't create acme domain")
	}
	this.PutAcmeAuthDomain(acmeHostName)

	challengeResponse, err := acmeutils.ChallengeResponseJSON(this.privateKey, challenge.Token, challenge.Type)
	if err == nil {
		logrus.Debugf("Create challenge response for domain '%v'", domain)
	} else {
		logrus.Errorf("Can't create challenge response for domain '%v', token '%v', challenge type %v: %v",
			domain, challenge.Token, challenge.Type, err)
		return nil, errors.New("Can't create challenge response")
	}
	for i := 0; i < TRY_COUNT; i++ {
		err = this.client.RespondToChallenge(challenge, challengeResponse, this.privateKey, ctx)
		if err == nil {
			break
		} else {
			logrus.Info("Can't send response for challenge of domain '%v': %v", domain, err)
			time.Sleep(RETRY_SLEEP)
		}
	}
	if err == nil {
		logrus.Debugf("Send challenge response for domain '%v'", domain)
	} else {
		logrus.Errorf("Can't send response for challenge of domain '%v': %v", domain, err)
		return nil, errors.New("Can't send response for challenge")
	}

	for i := 0; i < TRY_COUNT; i++ {
		err = this.client.LoadChallenge(challenge, ctx)
		if err != nil {
			logrus.Infof("Can't load challenge for domain '%v': %v", domain, err)
			time.Sleep(RETRY_SLEEP)
		}
	}
	if err == nil {
		logrus.Debugf("Load challenge for domain '%v'", domain)
	} else {
		logrus.Errorf("Can't load challenge for domain '%v': %v", domain, err)
		return nil, errors.New("Can't load challenge")
	}

	// Generate CSR
	certKey, err := rsa.GenerateKey(cryptorand.Reader, PRIVATE_KEY_BITS)
	if err == nil {
		logrus.Debugf("Create private key for domain '%v'", domain)
	} else {
		logrus.Errorf("Can't create rsa key for domain '%v': %v", err)
		return nil, errors.New("Can't create rsa key")
	}
	certRequest := &x509.CertificateRequest{
		SignatureAlgorithm: x509.SHA256WithRSA,
		PublicKeyAlgorithm: x509.RSA,
		PublicKey:          &certKey.PublicKey,
		Subject:            pkix.Name{CommonName: domain},
		DNSNames:           []string{domain},
	}
	csrDER, err := x509.CreateCertificateRequest(cryptorand.Reader, certRequest, certKey)
	if err == nil {
		logrus.Debugf("Create CSR for domain '%v'", domain)
	} else {
		logrus.Errorf("Can't create csr for domain '%v': %v", domain, err)
		return nil, errors.New("Can't create csr")
	}

	var certResponse *acmeapi.Certificate
	for i := 0; i < TRY_COUNT; i++ {
		certResponse, err = this.client.RequestCertificate(csrDER, ctx)
		if err != nil {
			logrus.Infof("Can't get certificate for domain '%v': %v", domain, err)
			time.Sleep(RETRY_SLEEP)
		}
	}
	if err == nil {
		logrus.Infof("Get certificate for domain '%v'", domain)
	} else {
		logrus.Errorf("Can't get certificate for domain '%v': %v", domain, err)
		return nil, errors.New("Can't request certificate")
	}
	cert = &tls.Certificate{}
	cert.Certificate = [][]byte{certResponse.Certificate}
	fmt.Printf("CERT:\n%s\n", certResponse.Certificate)
	cert.Leaf, err = x509.ParseCertificate(certResponse.Certificate)
	if err == nil {
		logrus.Debugf("Parse certificate for domain '%v'", domain)
	} else {
		logrus.Errorf("Can't parse certificate for domain '%v':%v", domain, err)
		return nil, errors.New("Can't parse certificate")
	}
	fmt.Printf("CERT PARSED\n%#v\n", cert)

	pemEncode := func(b []byte, t string) []byte {
		return pem.EncodeToMemory(&pem.Block{Bytes: b, Type: t})
	}
	certPEM := pem.EncodeToMemory(&pem.Block{Bytes: certResponse.Certificate, Type: "CERTIFICATE"})
	fmt.Printf("CERT PEM:\n%s\n", certPEM)
	certKeyPEM := pemEncode(x509.MarshalPKCS1PrivateKey(certKey), "RSA PRIVATE KEY")

	cert, err = tls.X509KeyPair(certPEM, certKeyPEM)
	if err == nil {
		logrus.Infof("Cert for domain '%v' parsed.", domain)
	} else {
		logrus.Errorf("Can't parse cert for domain '%v': %v", domain, err)
		return nil, errors.New("Can't parse cert for domain")
	}

	return cert, nil
}

func (this *acmeStruct) createCertificateSelfSigned(domain string) (cert *tls.Certificate, err error) {
	derCert, privateKey, err := acmeutils.CreateTLSSNICertificate(domain)
	if err != nil {
		logrus.Errorf("Can't create tls-sni-01 self-signed certificate for '%v': %v", domain, err)
		return nil, err
	}

	cert = &tls.Certificate{}
	cert.Certificate = [][]byte{derCert}
	cert.PrivateKey = privateKey
	return cert, nil
}

func (this *acmeStruct) PutAcmeAuthDomain(domain string) {
	this.acmeauthDomainsMutex.Lock()
	defer this.acmeauthDomainsMutex.Unlock()

	logrus.Debug("Put acme auth domain:", domain)
	this.acmeAuthDomains[domain] = time.Now().Add(SNI01_EXPIRE_TOKEN)
}

func (this *acmeStruct) DeleteAcmeAuthDomain(domain string) bool {
	this.acmeauthDomainsMutex.Lock()
	defer this.acmeauthDomainsMutex.Unlock()

	logrus.Debug("Delete acme auth domain:", domain)
	_, ok := this.acmeAuthDomains[domain]
	if ok {
		delete(this.acmeAuthDomains, domain)
	}
	return ok
}
<|endoftext|>


<commit_before>package realtime

import (
	"math/rand"
	"socialapi/models"
	"socialapi/workers/common/runner"
	"strconv"
	"testing"
	"time"

	"labix.org/v2/mgo/bson"

	. "github.com/smartystreets/goconvey/convey"
)

func TestChannelUpdatedCalculateUnreadItemCount(t *testing.T) {
	r := runner.New("test")
	if err := r.Init(); err != nil {
		t.Fatalf("couldnt start bongo %s", err.Error())
	}
	defer r.Close()

	rand.Seed(time.Now().UnixNano())
	groupName := "testgroup" + strconv.FormatInt(rand.Int63(), 10)

	Convey("while testing unread count", t, func() {
		Convey("channel should be set", func() {
			cue := &channelUpdatedEvent{}
			unreadCount, err := cue.calculateUnreadItemCount()
			So(err, ShouldNotBeNil)
			So(err, ShouldEqual, models.ErrChannelIsNotSet)
			So(unreadCount, ShouldEqual, 0)
		})

		Convey("unread count for group channel can not be calculated", func() {
			c := models.NewChannel()
			c.TypeConstant = models.Channel_TYPE_GROUP
			cue := &channelUpdatedEvent{
				Channel: c,
			}
			unreadCount, err := cue.calculateUnreadItemCount()
			So(err, ShouldNotBeNil)
			So(err.Error(), ShouldContainSubstring, "not supported channel type for unread count calculation")
			So(unreadCount, ShouldEqual, 0)
		})

		Convey("unread count for following feed channel can not be calculated", func() {
			c := models.NewChannel()
			c.TypeConstant = models.Channel_TYPE_FOLLOWINGFEED
			cue := &channelUpdatedEvent{
				Channel: c,
			}
			unreadCount, err := cue.calculateUnreadItemCount()
			So(err, ShouldNotBeNil)
			So(err.Error(), ShouldContainSubstring, "not supported channel type for unread count calculation")
			So(unreadCount, ShouldEqual, 0)
		})

		Convey("unread count for followers feed channel can not be calculated", func() {
			c := models.NewChannel()
			c.TypeConstant = models.Channel_TYPE_FOLLOWERS
			cue := &channelUpdatedEvent{
				Channel: c,
			}
			unreadCount, err := cue.calculateUnreadItemCount()
			So(err, ShouldNotBeNil)
			So(err.Error(), ShouldContainSubstring, "not supported channel type for unread count calculation")
			So(unreadCount, ShouldEqual, 0)
		})

		Convey("unread count for default channel can not be calculated", func() {
			c := models.NewChannel()
			c.TypeConstant = models.Channel_TYPE_DEFAULT
			cue := &channelUpdatedEvent{
				Channel: c,
			}
			unreadCount, err := cue.calculateUnreadItemCount()
			So(err, ShouldNotBeNil)
			So(err.Error(), ShouldContainSubstring, "not supported channel type for unread count calculation")
			So(unreadCount, ShouldEqual, 0)
		})

		Convey("channel participant should be set", func() {
			c := models.NewChannel()
			c.TypeConstant = models.Channel_TYPE_TOPIC
			cue := &channelUpdatedEvent{
				Channel: c,
			}
			unreadCount, err := cue.calculateUnreadItemCount()
			So(err, ShouldNotBeNil)
			So(err, ShouldEqual, models.ErrChannelParticipantIsNotSet)
			So(unreadCount, ShouldEqual, 0)
		})

		Convey("pinned message's unread count could be calculated", func() {
			// create an account
			account, err := createAccount()
			So(err, ShouldBeNil)
			So(account, ShouldNotBeNil)

			// create their pinned activity channel
			c, err := models.EnsurePinnedActivityChannel(account.Id, groupName)
			So(err, ShouldBeNil)
			So(c, ShouldNotBeNil)

			// fetch participant
			cp, err := c.FetchParticipant(account.Id)
			So(err, ShouldBeNil)
			So(cp, ShouldNotBeNil)

			// create message
			cm := models.NewChannelMessage()
			cm.AccountId = account.Id
			// this cahnnel should be group channel
			cm.InitialChannelId = c.Id
			cm.Body = "hello all from test"
			So(cm.Create(), ShouldBeNil)

			// add message to the list
			cml, err := c.AddMessage(cm.Id)
			So(err, ShouldBeNil)
			So(cml, ShouldNotBeNil)

			cue := &channelUpdatedEvent{
				Channel:              c,
				ChannelParticipant:   cp,
				ParentChannelMessage: cm,
			}

			unreadCount, err := cue.calculateUnreadItemCount()
			So(err, ShouldBeNil)
			So(unreadCount, ShouldEqual, 0)

			// add replies to the message
			addRepliesToMessage(c.Id, account.Id, cm.Id)
			addRepliesToMessage(c.Id, account.Id, cm.Id)

			cue = &channelUpdatedEvent{
				Channel:              c,
				ChannelParticipant:   cp,
				ParentChannelMessage: cm,
			}

			unreadCount, err = cue.calculateUnreadItemCount()
			So(err, ShouldBeNil)
			So(unreadCount, ShouldEqual, 2)

			// glance message
			cml.Glance()
			//after glancing the message, unread count should be zero

			cue = &channelUpdatedEvent{
				Channel:              c,
				ChannelParticipant:   cp,
				ParentChannelMessage: cm,
			}

			unreadCount, err = cue.calculateUnreadItemCount()
			So(err, ShouldBeNil)
			So(unreadCount, ShouldEqual, 0)
		})

		Convey("private message's unread count could be calculated", func() {
			// create an account
			account, err := createAccount()
			So(err, ShouldBeNil)
			So(account, ShouldNotBeNil)

			// create private message channel
			c := models.NewPrivateMessageChannel(account.Id, groupName)
			So(c.Create(), ShouldBeNil)

			// add participant into channel
			cp, err := c.AddParticipant(account.Id)
			So(err, ShouldBeNil)
			So(cp, ShouldNotBeNil)

			// create message
			cm := models.NewChannelMessage()
			cm.AccountId = account.Id
			cm.InitialChannelId = c.Id
			cm.Body = "hello all from test"
			So(cm.Create(), ShouldBeNil)

			// add message to the list
			cml, err := c.AddMessage(cm.Id)
			So(err, ShouldBeNil)
			So(cml, ShouldNotBeNil)

			cue := &channelUpdatedEvent{
				Channel:              c,
				ChannelParticipant:   cp,
				ParentChannelMessage: cm,
			}

			// calculate unread count
			unreadCount, err := cue.calculateUnreadItemCount()
			So(err, ShouldBeNil)
			So(unreadCount, ShouldEqual, 1)

			cp.LastSeenAt = time.Now().UTC()
			So(cp.Update(), ShouldBeNil)

			// calculate unread count
			unreadCount, err = cue.calculateUnreadItemCount()
			So(err, ShouldBeNil)
			So(unreadCount, ShouldEqual, 0)
		})

		Convey("topic channel's unread count could be calculated", func() {
			// create an account
			account, err := createAccount()
			So(err, ShouldBeNil)
			So(account, ShouldNotBeNil)

			// create private message channel
			c, err := createTypedChannel(account.Id, groupName, models.Channel_TYPE_TOPIC)
			So(err, ShouldBeNil)
			So(c.Create(), ShouldBeNil)

			// add participant into channel
			cp, err := c.AddParticipant(account.Id)
			So(err, ShouldBeNil)
			So(cp, ShouldNotBeNil)

			// create message
			cm := models.NewChannelMessage()
			cm.AccountId = account.Id
			cm.InitialChannelId = c.Id
			cm.Body = "hello all from test"
			So(cm.Create(), ShouldBeNil)

			// add message to the list
			cml, err := c.AddMessage(cm.Id)
			So(err, ShouldBeNil)
			So(cml, ShouldNotBeNil)

			cue := &channelUpdatedEvent{
				Channel:              c,
				ChannelParticipant:   cp,
				ParentChannelMessage: cm,
			}

			// calculate unread count
			unreadCount, err := cue.calculateUnreadItemCount()
			So(err, ShouldBeNil)
			So(unreadCount, ShouldEqual, 1)

			cp.LastSeenAt = time.Now().UTC()
			So(cp.Update(), ShouldBeNil)

			// calculate unread count
			unreadCount, err = cue.calculateUnreadItemCount()
			So(err, ShouldBeNil)
			So(unreadCount, ShouldEqual, 0)
		})

		Convey("announcement channel's unread count could be calculated", func() {
			// create an account
			account, err := createAccount()
			So(err, ShouldBeNil)
			So(account, ShouldNotBeNil)

			// create private message channel
			c, err := createTypedChannel(account.Id, groupName, models.Channel_TYPE_ANNOUNCEMENT)
			So(err, ShouldBeNil)
			So(c.Create(), ShouldBeNil)

			// add participant into channel
			cp, err := c.AddParticipant(account.Id)
			So(err, ShouldBeNil)
			So(cp, ShouldNotBeNil)

			// create message
			cm := models.NewChannelMessage()
			cm.AccountId = account.Id
			cm.InitialChannelId = c.Id
			cm.Body = "hello all from test"
			So(cm.Create(), ShouldBeNil)

			// add message to the list
			cml, err := c.AddMessage(cm.Id)
			So(err, ShouldBeNil)
			So(cml, ShouldNotBeNil)

			cue := &channelUpdatedEvent{
				Channel:              c,
				ChannelParticipant:   cp,
				ParentChannelMessage: cm,
			}

			// calculate unread count
			unreadCount, err := cue.calculateUnreadItemCount()
			So(err, ShouldBeNil)
			So(unreadCount, ShouldEqual, 1)

			cp.LastSeenAt = time.Now().UTC()
			So(cp.Update(), ShouldBeNil)

			// calculate unread count
			unreadCount, err = cue.calculateUnreadItemCount()
			So(err, ShouldBeNil)
			So(unreadCount, ShouldEqual, 0)
		})
	})
}

func addRepliesToMessage(channelId, accountId, messageId int64) {
	// add replies to the message
	rply1 := models.NewChannelMessage()
	rply1.AccountId = accountId
	rply1.InitialChannelId = channelId
	rply1.TypeConstant = models.ChannelMessage_TYPE_REPLY
	rply1.Body = "hello reply all from test"
	So(rply1.Create(), ShouldBeNil)

	mr := models.NewMessageReply()
	mr.ReplyId = rply1.Id
	mr.MessageId = messageId
	So(mr.Create(), ShouldBeNil)
}

// func createMessageWithTest() *ChannelMessage {
// 	cm := NewChannelMessage()
// 	// init account
// 	account, err := createAccount()
// 	So(err, ShouldBeNil)
// 	So(account, ShouldNotBeNil)
// 	So(account.Id, ShouldNotEqual, 0)
// 	// init channel
// 	channel, err := createChannel(account.Id)
// 	So(err, ShouldBeNil)
// 	So(channel, ShouldNotBeNil)
// 	// set account id
// 	cm.AccountId = account.Id
// 	// set channel id
// 	cm.InitialChannelId = channel.Id
// 	// set body
// 	cm.Body = "5five"
// 	return cm
// }

func createAccount() (*models.Account, error) {
	// create and account instance
	account := models.NewAccount()
	// create a fake mongo id
	oldId := bson.NewObjectId()
	// assign it to our test user
	account.OldId = oldId.Hex()
	account.Nick = oldId.Hex()
	if err := account.Create(); err != nil {
		return nil, err
	}

	return account, nil
}

func createTypedChannel(creatorId int64, groupName, typeConstant string) (*models.Channel, error) {
	// create and account instance
	channel := models.NewChannel()
	channel.CreatorId = creatorId
	channel.GroupName = groupName
	channel.TypeConstant = typeConstant

	if err := channel.Create(); err != nil {
		return nil, err
	}

	return channel, nil
}
<commit_msg>realtime: skip pinned message unread count tests<commit_after>package realtime

import (
	"math/rand"
	"socialapi/models"
	"socialapi/workers/common/runner"
	"strconv"
	"testing"
	"time"

	"labix.org/v2/mgo/bson"

	. "github.com/smartystreets/goconvey/convey"
)

func TestChannelUpdatedCalculateUnreadItemCount(t *testing.T) {
	r := runner.New("test")
	if err := r.Init(); err != nil {
		t.Fatalf("couldnt start bongo %s", err.Error())
	}
	defer r.Close()

	rand.Seed(time.Now().UnixNano())
	groupName := "testgroup" + strconv.FormatInt(rand.Int63(), 10)

	Convey("while testing unread count", t, func() {
		Convey("channel should be set", func() {
			cue := &channelUpdatedEvent{}
			unreadCount, err := cue.calculateUnreadItemCount()
			So(err, ShouldNotBeNil)
			So(err, ShouldEqual, models.ErrChannelIsNotSet)
			So(unreadCount, ShouldEqual, 0)
		})

		Convey("unread count for group channel can not be calculated", func() {
			c := models.NewChannel()
			c.TypeConstant = models.Channel_TYPE_GROUP
			cue := &channelUpdatedEvent{
				Channel: c,
			}
			unreadCount, err := cue.calculateUnreadItemCount()
			So(err, ShouldNotBeNil)
			So(err.Error(), ShouldContainSubstring, "not supported channel type for unread count calculation")
			So(unreadCount, ShouldEqual, 0)
		})

		Convey("unread count for following feed channel can not be calculated", func() {
			c := models.NewChannel()
			c.TypeConstant = models.Channel_TYPE_FOLLOWINGFEED
			cue := &channelUpdatedEvent{
				Channel: c,
			}
			unreadCount, err := cue.calculateUnreadItemCount()
			So(err, ShouldNotBeNil)
			So(err.Error(), ShouldContainSubstring, "not supported channel type for unread count calculation")
			So(unreadCount, ShouldEqual, 0)
		})

		Convey("unread count for followers feed channel can not be calculated", func() {
			c := models.NewChannel()
			c.TypeConstant = models.Channel_TYPE_FOLLOWERS
			cue := &channelUpdatedEvent{
				Channel: c,
			}
			unreadCount, err := cue.calculateUnreadItemCount()
			So(err, ShouldNotBeNil)
			So(err.Error(), ShouldContainSubstring, "not supported channel type for unread count calculation")
			So(unreadCount, ShouldEqual, 0)
		})

		Convey("unread count for default channel can not be calculated", func() {
			c := models.NewChannel()
			c.TypeConstant = models.Channel_TYPE_DEFAULT
			cue := &channelUpdatedEvent{
				Channel: c,
			}
			unreadCount, err := cue.calculateUnreadItemCount()
			So(err, ShouldNotBeNil)
			So(err.Error(), ShouldContainSubstring, "not supported channel type for unread count calculation")
			So(unreadCount, ShouldEqual, 0)
		})

		Convey("channel participant should be set", func() {
			c := models.NewChannel()
			c.TypeConstant = models.Channel_TYPE_TOPIC
			cue := &channelUpdatedEvent{
				Channel: c,
			}
			unreadCount, err := cue.calculateUnreadItemCount()
			So(err, ShouldNotBeNil)
			So(err, ShouldEqual, models.ErrChannelParticipantIsNotSet)
			So(unreadCount, ShouldEqual, 0)
		})

		SkipConvey("pinned message's unread count could be calculated", func() {
			// create an account
			account, err := createAccount()
			So(err, ShouldBeNil)
			So(account, ShouldNotBeNil)

			// create their pinned activity channel
			c, err := models.EnsurePinnedActivityChannel(account.Id, groupName)
			So(err, ShouldBeNil)
			So(c, ShouldNotBeNil)

			// fetch participant
			cp, err := c.FetchParticipant(account.Id)
			So(err, ShouldBeNil)
			So(cp, ShouldNotBeNil)

			// create message
			cm := models.NewChannelMessage()
			cm.AccountId = account.Id
			// this cahnnel should be group channel
			cm.InitialChannelId = c.Id
			cm.Body = "hello all from test"
			So(cm.Create(), ShouldBeNil)

			// add message to the list
			cml, err := c.AddMessage(cm.Id)
			So(err, ShouldBeNil)
			So(cml, ShouldNotBeNil)

			cue := &channelUpdatedEvent{
				Channel:              c,
				ChannelParticipant:   cp,
				ParentChannelMessage: cm,
			}

			unreadCount, err := cue.calculateUnreadItemCount()
			So(err, ShouldBeNil)
			So(unreadCount, ShouldEqual, 0)

			// add replies to the message
			addRepliesToMessage(c.Id, account.Id, cm.Id)
			addRepliesToMessage(c.Id, account.Id, cm.Id)

			cue = &channelUpdatedEvent{
				Channel:              c,
				ChannelParticipant:   cp,
				ParentChannelMessage: cm,
			}

			unreadCount, err = cue.calculateUnreadItemCount()
			So(err, ShouldBeNil)
			So(unreadCount, ShouldEqual, 2)

			// glance message
			cml.Glance()
			//after glancing the message, unread count should be zero

			cue = &channelUpdatedEvent{
				Channel:              c,
				ChannelParticipant:   cp,
				ParentChannelMessage: cm,
			}

			unreadCount, err = cue.calculateUnreadItemCount()
			So(err, ShouldBeNil)
			So(unreadCount, ShouldEqual, 0)
		})

		Convey("private message's unread count could be calculated", func() {
			// create an account
			account, err := createAccount()
			So(err, ShouldBeNil)
			So(account, ShouldNotBeNil)

			// create private message channel
			c := models.NewPrivateMessageChannel(account.Id, groupName)
			So(c.Create(), ShouldBeNil)

			// add participant into channel
			cp, err := c.AddParticipant(account.Id)
			So(err, ShouldBeNil)
			So(cp, ShouldNotBeNil)

			// create message
			cm := models.NewChannelMessage()
			cm.AccountId = account.Id
			cm.InitialChannelId = c.Id
			cm.Body = "hello all from test"
			So(cm.Create(), ShouldBeNil)

			// add message to the list
			cml, err := c.AddMessage(cm.Id)
			So(err, ShouldBeNil)
			So(cml, ShouldNotBeNil)

			cue := &channelUpdatedEvent{
				Channel:              c,
				ChannelParticipant:   cp,
				ParentChannelMessage: cm,
			}

			// calculate unread count
			unreadCount, err := cue.calculateUnreadItemCount()
			So(err, ShouldBeNil)
			So(unreadCount, ShouldEqual, 1)

			cp.LastSeenAt = time.Now().UTC()
			So(cp.Update(), ShouldBeNil)

			// calculate unread count
			unreadCount, err = cue.calculateUnreadItemCount()
			So(err, ShouldBeNil)
			So(unreadCount, ShouldEqual, 0)
		})

		Convey("topic channel's unread count could be calculated", func() {
			// create an account
			account, err := createAccount()
			So(err, ShouldBeNil)
			So(account, ShouldNotBeNil)

			// create private message channel
			c, err := createTypedChannel(account.Id, groupName, models.Channel_TYPE_TOPIC)
			So(err, ShouldBeNil)
			So(c.Create(), ShouldBeNil)

			// add participant into channel
			cp, err := c.AddParticipant(account.Id)
			So(err, ShouldBeNil)
			So(cp, ShouldNotBeNil)

			// create message
			cm := models.NewChannelMessage()
			cm.AccountId = account.Id
			cm.InitialChannelId = c.Id
			cm.Body = "hello all from test"
			So(cm.Create(), ShouldBeNil)

			// add message to the list
			cml, err := c.AddMessage(cm.Id)
			So(err, ShouldBeNil)
			So(cml, ShouldNotBeNil)

			cue := &channelUpdatedEvent{
				Channel:              c,
				ChannelParticipant:   cp,
				ParentChannelMessage: cm,
			}

			// calculate unread count
			unreadCount, err := cue.calculateUnreadItemCount()
			So(err, ShouldBeNil)
			So(unreadCount, ShouldEqual, 1)

			cp.LastSeenAt = time.Now().UTC()
			So(cp.Update(), ShouldBeNil)

			// calculate unread count
			unreadCount, err = cue.calculateUnreadItemCount()
			So(err, ShouldBeNil)
			So(unreadCount, ShouldEqual, 0)
		})

		Convey("announcement channel's unread count could be calculated", func() {
			// create an account
			account, err := createAccount()
			So(err, ShouldBeNil)
			So(account, ShouldNotBeNil)

			// create private message channel
			c, err := createTypedChannel(account.Id, groupName, models.Channel_TYPE_ANNOUNCEMENT)
			So(err, ShouldBeNil)
			So(c.Create(), ShouldBeNil)

			// add participant into channel
			cp, err := c.AddParticipant(account.Id)
			So(err, ShouldBeNil)
			So(cp, ShouldNotBeNil)

			// create message
			cm := models.NewChannelMessage()
			cm.AccountId = account.Id
			cm.InitialChannelId = c.Id
			cm.Body = "hello all from test"
			So(cm.Create(), ShouldBeNil)

			// add message to the list
			cml, err := c.AddMessage(cm.Id)
			So(err, ShouldBeNil)
			So(cml, ShouldNotBeNil)

			cue := &channelUpdatedEvent{
				Channel:              c,
				ChannelParticipant:   cp,
				ParentChannelMessage: cm,
			}

			// calculate unread count
			unreadCount, err := cue.calculateUnreadItemCount()
			So(err, ShouldBeNil)
			So(unreadCount, ShouldEqual, 1)

			cp.LastSeenAt = time.Now().UTC()
			So(cp.Update(), ShouldBeNil)

			// calculate unread count
			unreadCount, err = cue.calculateUnreadItemCount()
			So(err, ShouldBeNil)
			So(unreadCount, ShouldEqual, 0)
		})
	})
}

func addRepliesToMessage(channelId, accountId, messageId int64) {
	// add replies to the message
	rply1 := models.NewChannelMessage()
	rply1.AccountId = accountId
	rply1.InitialChannelId = channelId
	rply1.TypeConstant = models.ChannelMessage_TYPE_REPLY
	rply1.Body = "hello reply all from test"
	So(rply1.Create(), ShouldBeNil)

	mr := models.NewMessageReply()
	mr.ReplyId = rply1.Id
	mr.MessageId = messageId
	So(mr.Create(), ShouldBeNil)
}

// func createMessageWithTest() *ChannelMessage {
// 	cm := NewChannelMessage()
// 	// init account
// 	account, err := createAccount()
// 	So(err, ShouldBeNil)
// 	So(account, ShouldNotBeNil)
// 	So(account.Id, ShouldNotEqual, 0)
// 	// init channel
// 	channel, err := createChannel(account.Id)
// 	So(err, ShouldBeNil)
// 	So(channel, ShouldNotBeNil)
// 	// set account id
// 	cm.AccountId = account.Id
// 	// set channel id
// 	cm.InitialChannelId = channel.Id
// 	// set body
// 	cm.Body = "5five"
// 	return cm
// }

func createAccount() (*models.Account, error) {
	// create and account instance
	account := models.NewAccount()
	// create a fake mongo id
	oldId := bson.NewObjectId()
	// assign it to our test user
	account.OldId = oldId.Hex()
	account.Nick = oldId.Hex()
	if err := account.Create(); err != nil {
		return nil, err
	}

	return account, nil
}

func createTypedChannel(creatorId int64, groupName, typeConstant string) (*models.Channel, error) {
	// create and account instance
	channel := models.NewChannel()
	channel.CreatorId = creatorId
	channel.GroupName = groupName
	channel.TypeConstant = typeConstant

	if err := channel.Create(); err != nil {
		return nil, err
	}

	return channel, nil
}
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"
	"github.com/Scribblerockerz/cryptletter/cmd/cryptletter"
	"github.com/Scribblerockerz/cryptletter/cmd/initConfig"
	"os"

	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

var cfgFile string

// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
	Use:   "cryptletter",
	Short: "Encrypted self-destructing messages",
	Long: `Cryptletter is a tiny service to exchange information securely.`,
}

// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
	if err := rootCmd.Execute(); err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}

func init() {
	cobra.OnInitialize(initializeConfiguration)

	// Here you will define your flags and configuration settings.
	// Cobra supports persistent flags, which, if defined here,
	// will be global for your application.
	rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is cryptletter.yaml)")

	// Add commands
	rootCmd.AddCommand(cryptletter.NewCmd())
	//rootCmd.AddCommand(analyzeUrl.NewCmd())
	rootCmd.AddCommand(initConfig.NewCmd())
}

// initializeConfiguration reads in config file and ENV variables if set.
func initializeConfiguration() {
	if cfgFile != "" {
		viper.SetConfigFile(cfgFile)
	} else {
		viper.AddConfigPath(".")
		viper.SetConfigName("cryptletter")
		viper.SetConfigType("yaml")
	}

	viper.AutomaticEnv() // read in environment variables that match

	// If a config file is found, read it in.
	if err := viper.ReadInConfig(); err == nil {
		fmt.Println("Using config file:", viper.ConfigFileUsed())
	}
}
<commit_msg>Fix automatic env mapping<commit_after>package cmd

import (
	"fmt"
	"github.com/Scribblerockerz/cryptletter/cmd/cryptletter"
	"github.com/Scribblerockerz/cryptletter/cmd/initConfig"
	"os"
	"strings"

	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

var cfgFile string

// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
	Use:   "cryptletter",
	Short: "Encrypted self-destructing messages",
	Long: `Cryptletter is a tiny service to exchange information securely.`,
}

// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
	if err := rootCmd.Execute(); err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}

func init() {
	cobra.OnInitialize(initializeConfiguration)

	// Here you will define your flags and configuration settings.
	// Cobra supports persistent flags, which, if defined here,
	// will be global for your application.
	rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is cryptletter.yaml)")

	// Add commands
	rootCmd.AddCommand(cryptletter.NewCmd())
	//rootCmd.AddCommand(analyzeUrl.NewCmd())
	rootCmd.AddCommand(initConfig.NewCmd())
}

// initializeConfiguration reads in config file and ENV variables if set.
func initializeConfiguration() {
	if cfgFile != "" {
		viper.SetConfigFile(cfgFile)
	} else {
		viper.AddConfigPath(".")
		viper.SetConfigName("cryptletter")
		viper.SetConfigType("yaml")
	}

	viper.AutomaticEnv() // read in environment variables that match
	// replaces APP__ENV to app.env
	viper.SetEnvKeyReplacer(strings.NewReplacer(".", "__"))

	// If a config file is found, read it in.
	if err := viper.ReadInConfig(); err == nil {
		fmt.Println("Using config file:", viper.ConfigFileUsed())
	}
}
<|endoftext|>


<commit_before>package git

import (
	"log"
	"os"
	"os/exec"
	"testing"
)

func shell(path string) {
	sh := exec.Command("sh", "-c", path)
	sh.Stdout = os.Stdout
	sh.Stdin = os.Stdin
	if err := sh.Run(); err != nil {
		log.Fatal(err)
	}
}

func init() {
	shell("scripts/init")
}

func TestBranch(t *testing.T) {
	var (
		expect = "master"
		got    = ""
	)
	got, _ = Branch("testdata")
	if expect != got {
		t.Logf("expect '%s' got '%s'", expect, got)
	}
}

func TestBranchFail(t *testing.T) {
	var (
		expect = "nobranch"
		got    = ""
	)
	got, _ = Branch("testdata")
	if expect != got {
		t.Errorf("expect '%s' got '%s'", expect, got)
	}
}
<commit_msg>cleanup testdata<commit_after>package git

import (
	"log"
	"os"
	"os/exec"
	"testing"
)

func shell(path string) {
	sh := exec.Command("sh", "-c", path)
	sh.Stdout = os.Stdout
	sh.Stdin = os.Stdin
	if err := sh.Run(); err != nil {
		log.Fatal(err)
	}
}

func init() {
	shell("scripts/init")
}

func TestBranch(t *testing.T) {
	var (
		expect = "master"
		got    = ""
	)
	got, _ = Branch("testdata")
	if expect != got {
		t.Logf("expect '%s' got '%s'", expect, got)
	}
}

func TestBranchFail(t *testing.T) {
	var (
		expect = "nobranch"
		got    = ""
	)
	got, _ = Branch("testdata")
	if expect == got {
		t.Errorf("expect '%s' got '%s'", expect, got)
	}
}

func TestCleanup(t *testing.T) {
	err := os.RemoveAll("testdata")
	if err != nil {
		t.Error(err)
	}
}
<|endoftext|>