<commit_before>package integration_test

import (
	"fmt"
	"github.com/APTrust/exchange/constants"
	"github.com/APTrust/exchange/context"
	"github.com/APTrust/exchange/models"
	"github.com/APTrust/exchange/network"
	"github.com/APTrust/exchange/util"
	"github.com/APTrust/exchange/util/testutil"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"net/url"
	"os"
	"strings"
	"testing"
	"time"
)

// These tests verify the outcomes of a bag update.
// The bag in testdata/unit_test_bags/updated/example.edu.tagsample_good.tar
// is an altered version of testdata/unit_test_bags/example.edu.tagsample_good.tar,
// with one new file, one deleted file, and one changed file.
// We want to make sure all operations were peformed correctly in
// Pharos, S3, and Glacier.
//
// 1. Ensure unchanged files are not changed in Pharos, S3, Glacier.
// 2. Ensure new file is present in Pharos, S3, Glacier,
// 3. Ensure updated file is actually update in S3 & Glacier.
// 4. Ensure updated file has new checksum in Pharos.
// 5. Ensure updated file has new ingest & fixity calculation events in Pharos.
// 6. Ensure ingest work item was marked complete in Pharos.

const (
	UPDATED_BAG_IDENTIFIER = "test.edu/example.edu.tagsample_good"
	UPDATED_BAG_ETAG       = "a08e2b0b3ba3490434c23a8d78b1760f"
)

func TestUpdatedItemsInPharos(t *testing.T) {
	if !testutil.ShouldRunIntegrationTests() {
		t.Skip("Skipping integration test. Set ENV var RUN_EXCHANGE_INTEGRATION=true if you want to run them.")
	}
	_context, err := testutil.GetContext("integration.json")
	require.Nil(t, err)
	testUpdatedWorkItem(t, _context)
	testUpdatedObject(t, _context)
}

func testUpdatedWorkItem(t *testing.T, _context *context.Context) {
	params := url.Values{}
	params.Set("item_action", constants.ActionIngest)
	params.Set("object_identifier", UPDATED_BAG_IDENTIFIER)
	params.Set("etag", UPDATED_BAG_ETAG)
	params.Set("page", "1")
	params.Set("per_page", "1")

	// There will be two ingest work items for this
	// bag, but only one will match the etag we specified.
	resp := _context.PharosClient.WorkItemList(params)
	require.Nil(t, resp.Error)
	workItem := resp.WorkItem()

	require.NotNil(t, workItem, "WorkItem is missing for %s", UPDATED_BAG_IDENTIFIER)
	assert.Equal(t, constants.StatusSuccess, workItem.Status)
	assert.Equal(t, constants.StageCleanup, workItem.Stage)
	assert.Equal(t, "Item was successfully ingested", workItem.Note)
	assert.Empty(t, workItem.Node)
	assert.Equal(t, 0, workItem.Pid)
}

func testUpdatedObject(t *testing.T, _context *context.Context) {
	resp := _context.PharosClient.IntellectualObjectGet(UPDATED_BAG_IDENTIFIER, true, true)
	obj := resp.IntellectualObject()
	require.NotNil(t, obj)

	ingestEvents := obj.FindEventsByType(constants.EventIngestion)
	fileLevelEventCount := 0
	objectLevelEventCount := 0
	for _, event := range ingestEvents {
		if strings.HasPrefix(event.Detail, "Copied all files to perservation bucket") {
			objectLevelEventCount++
		} else if strings.HasPrefix(event.Detail, "Completed copy to S3") {
			fileLevelEventCount++
		}
	}
	assert.Equal(t, 2, objectLevelEventCount)
	assert.Equal(t, 15, fileLevelEventCount)
	assert.Empty(t, obj.FindEventsByType(constants.EventDeletion))
	require.Equal(t, 12, len(obj.GenericFiles))

	testUpdatedFilesInPharos(t, _context, obj)
}

func testUpdatedFilesInPharos(t *testing.T, _context *context.Context, obj *models.IntellectualObject) {
	// The updated file is different in the new version of the bag.
	// We have to make sure we've stored this and have the new
	// checksum attached to it. It should have two events for ingest,
	// and fixity generation.
	updatedFile := obj.FindGenericFile("data/datastream-DC")
	require.NotNil(t, updatedFile)
	assert.Equal(t, 2, len(updatedFile.FindEventsByType(constants.EventIngestion)))
	assert.Equal(t, 2, len(updatedFile.FindEventsByType(constants.EventDigestCalculation)))
	assert.Equal(t, 2, len(updatedFile.FindEventsByType(constants.EventReplication)))
	require.Equal(t, 4, len(updatedFile.Checksums))

	actualDigests := []string{
		updatedFile.Checksums[0].Digest,
		updatedFile.Checksums[1].Digest,
		updatedFile.Checksums[2].Digest,
		updatedFile.Checksums[3].Digest,
	}
	expectedDigests := []string{
		"44d85cf4810d6c6fe87750117633e461",
		"248fac506a5c46b3c760312b99827b6fb5df4698d6cf9a9cdc4c54746728ab99",
		"7a31f705fc1a16571374406c5a9b7681",
		"baf8752080187b1e401ae952047029ae4e16b5f54c5daf9d97bc0c7598772326",
	}

	// Digests may be in unexpected order. Make sure they're all present.
	for _, expectedDigest := range expectedDigests {
		assert.True(t, util.StringListContains(actualDigests, expectedDigest),
			"Digest %s not found", expectedDigest)
	}

	// require.Equal(t, "44d85cf4810d6c6fe87750117633e461", updatedFile.Checksums[0].Digest)
	// require.Equal(t, "248fac506a5c46b3c760312b99827b6fb5df4698d6cf9a9cdc4c54746728ab99",
	//	updatedFile.Checksums[1].Digest)
	// require.Equal(t, "7a31f705fc1a16571374406c5a9b7681", updatedFile.Checksums[2].Digest)
	// require.Equal(t, "baf8752080187b1e401ae952047029ae4e16b5f54c5daf9d97bc0c7598772326",
	//	updatedFile.Checksums[3].Digest)

	testUpdatedFileInStorage(t, _context, updatedFile)

	// The new file is in the new version of the bag, but was not
	// in the original version.
	newFile := obj.FindGenericFile("data/new_file.txt")
	require.NotNil(t, newFile)
	testUpdatedFileInStorage(t, _context, newFile)

	// The deleted file was in the original version of the bag, but
	// is not in the new version. We DO NOT delete this, since only
	// the depositing institution can delete an item. Make sure
	// it's still there.
	deletedFile := obj.FindGenericFile("data/datastream-RELS-EXT")
	require.NotNil(t, deletedFile)
	testUpdatedFileInStorage(t, _context, deletedFile)
}

func testUpdatedFileInStorage(t *testing.T, _context *context.Context, gf *models.GenericFile) {
	// Set up an S3 client to look at the S3 bucket, and a Glacier client
	// to look at the Glacier bucket.
	clients := make(map[string]*network.S3Head)
	clients["s3"] = network.NewS3Head(
		os.Getenv("AWS_ACCESS_KEY_ID"),
		os.Getenv("AWS_SECRET_ACCESS_KEY"),
		_context.Config.APTrustS3Region,
		_context.Config.PreservationBucket)
	clients["glacier"] = network.NewS3Head(
		os.Getenv("AWS_ACCESS_KEY_ID"),
		os.Getenv("AWS_SECRET_ACCESS_KEY"),
		_context.Config.APTrustGlacierRegion,
		_context.Config.ReplicationBucket)

	// This is the name of the file in S3/Glacier
	uuid, err := gf.PreservationStorageFileName()
	require.Nil(t, err, gf.Identifier)

	// Make sure file is present and all metadata is set correctly.
	for _, client := range clients {
		idAndUuid := fmt.Sprintf("%s (%s)", gf.Identifier, uuid)
		client.Head(uuid)
		require.Empty(t, client.ErrorMessage, idAndUuid)
		storedFile := client.StoredFile()
		require.NotNil(t, storedFile, idAndUuid)

		institution, _ := gf.InstitutionIdentifier()
		timeSinceLastModified := time.Since(storedFile.LastModified)
		assert.True(t, timeSinceLastModified < (10*time.Minute))
		assert.EqualValues(t, gf.Size, storedFile.Size, idAndUuid)
		assert.Equal(t, institution, storedFile.Institution, idAndUuid)
		assert.Equal(t, gf.IntellectualObjectIdentifier, storedFile.BagName, idAndUuid)
		assert.Equal(t, gf.OriginalPath(), storedFile.PathInBag, idAndUuid)
		assert.Equal(t, gf.GetChecksumByAlgorithm(constants.AlgMd5).Digest, storedFile.Md5, idAndUuid)
		assert.Equal(t, gf.GetChecksumByAlgorithm(constants.AlgSha256).Digest, storedFile.Sha256, idAndUuid)
	}
}
<commit_msg>Started ingest-updated post tests for Glacier-only items. Currently not passing.<commit_after>package integration_test

import (
	"fmt"
	"github.com/APTrust/exchange/constants"
	"github.com/APTrust/exchange/context"
	"github.com/APTrust/exchange/models"
	"github.com/APTrust/exchange/network"
	"github.com/APTrust/exchange/util"
	"github.com/APTrust/exchange/util/testutil"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"net/url"
	"os"
	"strings"
	"testing"
	"time"
)

// These tests verify the outcomes of a bag update.
// The bag in testdata/unit_test_bags/updated/example.edu.tagsample_good.tar
// is an altered version of testdata/unit_test_bags/example.edu.tagsample_good.tar,
// with one new file, one deleted file, and one changed file.
// We want to make sure all operations were peformed correctly in
// Pharos, S3, and Glacier.
//
// 1. Ensure unchanged files are not changed in Pharos, S3, Glacier.
// 2. Ensure new file is present in Pharos, S3, Glacier,
// 3. Ensure updated file is actually update in S3 & Glacier.
// 4. Ensure updated file has new checksum in Pharos.
// 5. Ensure updated file has new ingest & fixity calculation events in Pharos.
// 6. Ensure ingest work item was marked complete in Pharos.

const (
	UPDATED_BAG_IDENTIFIER = "test.edu/example.edu.tagsample_good"
	UPDATED_BAG_ETAG       = "a08e2b0b3ba3490434c23a8d78b1760f"

	UPDATED_GLACIER_BAG_IDENTIFIER = "test.edu/example.edu.sample_glacier_oh"
	UPDATED_GLACIER_BAG_ETAG       = "bf01126663915a4f5d135a37443b8349"
)

func TestUpdatedItemsInPharos(t *testing.T) {
	if !testutil.ShouldRunIntegrationTests() {
		t.Skip("Skipping integration test. Set ENV var RUN_EXCHANGE_INTEGRATION=true if you want to run them.")
	}
	_context, err := testutil.GetContext("integration.json")
	require.Nil(t, err)
	testUpdatedWorkItem(t, _context, UPDATED_BAG_IDENTIFIER, UPDATED_BAG_ETAG)
	testUpdatedWorkItem(t, _context, UPDATED_GLACIER_BAG_IDENTIFIER, UPDATED_GLACIER_BAG_ETAG)
	testUpdatedObject(t, _context)
	testUpdatedGlacierObject(t, _context)
}

func testUpdatedWorkItem(t *testing.T, _context *context.Context, bagIdentifier, etag string) {
	params := url.Values{}
	params.Set("item_action", constants.ActionIngest)
	params.Set("object_identifier", bagIdentifier)
	params.Set("etag", etag)
	params.Set("page", "1")
	params.Set("per_page", "1")

	// There will be two ingest work items for this
	// bag, but only one will match the etag we specified.
	resp := _context.PharosClient.WorkItemList(params)
	require.Nil(t, resp.Error)
	workItem := resp.WorkItem()

	require.NotNil(t, workItem, "WorkItem is missing for %s", bagIdentifier)
	assert.Equal(t, constants.StatusSuccess, workItem.Status)
	assert.Equal(t, constants.StageCleanup, workItem.Stage)
	assert.Equal(t, "Item was successfully ingested", workItem.Note)
	assert.Empty(t, workItem.Node)
	assert.Equal(t, 0, workItem.Pid)
}

func testUpdatedObject(t *testing.T, _context *context.Context) {
	resp := _context.PharosClient.IntellectualObjectGet(UPDATED_BAG_IDENTIFIER, true, true)
	obj := resp.IntellectualObject()
	require.NotNil(t, obj)

	ingestEvents := obj.FindEventsByType(constants.EventIngestion)
	fileLevelEventCount := 0
	objectLevelEventCount := 0
	for _, event := range ingestEvents {
		if strings.HasPrefix(event.Detail, "Copied all files to perservation bucket") {
			objectLevelEventCount++
		} else if strings.HasPrefix(event.Detail, "Completed copy to S3") {
			fileLevelEventCount++
		}
	}
	assert.Equal(t, 2, objectLevelEventCount)
	assert.Equal(t, 15, fileLevelEventCount)
	assert.Empty(t, obj.FindEventsByType(constants.EventDeletion))
	require.Equal(t, 12, len(obj.GenericFiles))
	assert.Equal(t, constants.StorageStandard, obj.StorageOption)

	testUpdatedFilesInPharos(t, _context, obj)
}

func testUpdatedFilesInPharos(t *testing.T, _context *context.Context, obj *models.IntellectualObject) {
	// The updated file is different in the new version of the bag.
	// We have to make sure we've stored this and have the new
	// checksum attached to it. It should have two events for ingest,
	// and fixity generation.
	updatedFile := obj.FindGenericFile("data/datastream-DC")
	require.NotNil(t, updatedFile)
	assert.Equal(t, constants.StorageStandard, updatedFile.StorageOption)
	assert.Equal(t, 2, len(updatedFile.FindEventsByType(constants.EventIngestion)))
	assert.Equal(t, 2, len(updatedFile.FindEventsByType(constants.EventDigestCalculation)))
	assert.Equal(t, 2, len(updatedFile.FindEventsByType(constants.EventReplication)))
	require.Equal(t, 4, len(updatedFile.Checksums))

	actualDigests := []string{
		updatedFile.Checksums[0].Digest,
		updatedFile.Checksums[1].Digest,
		updatedFile.Checksums[2].Digest,
		updatedFile.Checksums[3].Digest,
	}
	expectedDigests := []string{
		"44d85cf4810d6c6fe87750117633e461",
		"248fac506a5c46b3c760312b99827b6fb5df4698d6cf9a9cdc4c54746728ab99",
		"7a31f705fc1a16571374406c5a9b7681",
		"baf8752080187b1e401ae952047029ae4e16b5f54c5daf9d97bc0c7598772326",
	}

	// Digests may be in unexpected order. Make sure they're all present.
	for _, expectedDigest := range expectedDigests {
		assert.True(t, util.StringListContains(actualDigests, expectedDigest),
			"Digest %s not found", expectedDigest)
	}

	testUpdatedFileInStorage(t, _context, updatedFile)

	// The new file is in the new version of the bag, but was not
	// in the original version.
	newFile := obj.FindGenericFile("data/new_file.txt")
	require.NotNil(t, newFile)
	testUpdatedFileInStorage(t, _context, newFile)

	// The deleted file was in the original version of the bag, but
	// is not in the new version. We DO NOT delete this, since only
	// the depositing institution can delete an item. Make sure
	// it's still there.
	deletedFile := obj.FindGenericFile("data/datastream-RELS-EXT")
	require.NotNil(t, deletedFile)
	testUpdatedFileInStorage(t, _context, deletedFile)
}

func testUpdatedFileInStorage(t *testing.T, _context *context.Context, gf *models.GenericFile) {
	// Set up an S3 client to look at the S3 bucket, and a Glacier client
	// to look at the Glacier bucket.
	clients := make(map[string]*network.S3Head)
	clients["s3"] = network.NewS3Head(
		os.Getenv("AWS_ACCESS_KEY_ID"),
		os.Getenv("AWS_SECRET_ACCESS_KEY"),
		_context.Config.APTrustS3Region,
		_context.Config.PreservationBucket)
	clients["glacier"] = network.NewS3Head(
		os.Getenv("AWS_ACCESS_KEY_ID"),
		os.Getenv("AWS_SECRET_ACCESS_KEY"),
		_context.Config.APTrustGlacierRegion,
		_context.Config.ReplicationBucket)

	// This is the name of the file in S3/Glacier
	uuid, err := gf.PreservationStorageFileName()
	require.Nil(t, err, gf.Identifier)

	// Make sure file is present and all metadata is set correctly.
	for _, client := range clients {
		idAndUuid := fmt.Sprintf("%s (%s)", gf.Identifier, uuid)
		client.Head(uuid)
		require.Empty(t, client.ErrorMessage, idAndUuid)
		storedFile := client.StoredFile()
		require.NotNil(t, storedFile, idAndUuid)

		institution, _ := gf.InstitutionIdentifier()
		timeSinceLastModified := time.Since(storedFile.LastModified)
		assert.True(t, timeSinceLastModified < (10*time.Minute))
		assert.EqualValues(t, gf.Size, storedFile.Size, idAndUuid)
		assert.Equal(t, institution, storedFile.Institution, idAndUuid)
		assert.Equal(t, gf.IntellectualObjectIdentifier, storedFile.BagName, idAndUuid)
		assert.Equal(t, gf.OriginalPath(), storedFile.PathInBag, idAndUuid)
		assert.Equal(t, gf.GetChecksumByAlgorithm(constants.AlgMd5).Digest, storedFile.Md5, idAndUuid)
		assert.Equal(t, gf.GetChecksumByAlgorithm(constants.AlgSha256).Digest, storedFile.Sha256, idAndUuid)
	}
}

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

func testUpdatedGlacierObject(t *testing.T, _context *context.Context) {
	resp := _context.PharosClient.IntellectualObjectGet(UPDATED_GLACIER_BAG_IDENTIFIER, true, true)
	obj := resp.IntellectualObject()
	require.NotNil(t, obj)

	ingestEvents := obj.FindEventsByType(constants.EventIngestion)
	fileLevelEventCount := 0
	objectLevelEventCount := 0
	for _, event := range ingestEvents {
		if strings.HasPrefix(event.Detail, "Copied all files to perservation bucket") {
			objectLevelEventCount++
		} else if strings.HasPrefix(event.Detail, "Completed copy to") {
			fileLevelEventCount++
		}
	}
	assert.Equal(t, 2, objectLevelEventCount)
	assert.Equal(t, 15, fileLevelEventCount) // What should this count be??
	assert.Empty(t, obj.FindEventsByType(constants.EventDeletion))
	require.Equal(t, 6, len(obj.GenericFiles))
	assert.Equal(t, constants.StorageGlacierOH, obj.StorageOption)

	testUpdatedGlacierFilesInPharos(t, _context, obj)
}

func testUpdatedGlacierFilesInPharos(t *testing.T, _context *context.Context, obj *models.IntellectualObject) {
	// The updated file is different in the new version of the bag.
	// We have to make sure we've stored this and have the new
	// checksum attached to it. It should have two events for ingest,
	// and fixity generation.
	updatedFile := obj.FindGenericFile("data/datastream-DC")
	require.NotNil(t, updatedFile)
	assert.Equal(t, constants.StorageGlacierOH, updatedFile.StorageOption)
	assert.Equal(t, 2, len(updatedFile.FindEventsByType(constants.EventIngestion)))
	assert.Equal(t, 2, len(updatedFile.FindEventsByType(constants.EventDigestCalculation)))
	assert.Equal(t, 0, len(updatedFile.FindEventsByType(constants.EventReplication)))
	require.Equal(t, 4, len(updatedFile.Checksums))

	actualDigests := []string{
		updatedFile.Checksums[0].Digest,
		updatedFile.Checksums[1].Digest,
		updatedFile.Checksums[2].Digest,
		updatedFile.Checksums[3].Digest,
	}
	expectedDigests := []string{
		"44d85cf4810d6c6fe87750117633e461",
		"248fac506a5c46b3c760312b99827b6fb5df4698d6cf9a9cdc4c54746728ab99",
		"7a31f705fc1a16571374406c5a9b7681",
		"baf8752080187b1e401ae952047029ae4e16b5f54c5daf9d97bc0c7598772326",
	}

	// Digests may be in unexpected order. Make sure they're all present.
	for _, expectedDigest := range expectedDigests {
		assert.True(t, util.StringListContains(actualDigests, expectedDigest),
			"Digest %s not found", expectedDigest)
	}

	testUpdatedGlacierFileInStorage(t, _context, updatedFile)

	otherUpdatedFile := obj.FindGenericFile("data/datastream-RELS-EXT")
	require.NotNil(t, otherUpdatedFile)
	// TODO: Test checksums for this
	testUpdatedGlacierFileInStorage(t, _context, otherUpdatedFile)
}

func testUpdatedGlacierFileInStorage(t *testing.T, _context *context.Context, gf *models.GenericFile) {
	client := network.NewS3Head(
		os.Getenv("AWS_ACCESS_KEY_ID"),
		os.Getenv("AWS_SECRET_ACCESS_KEY"),
		_context.Config.GlacierRegionOH,
		_context.Config.GlacierBucketOH)

	// This is the name of the file in S3/Glacier
	uuid, err := gf.PreservationStorageFileName()
	require.Nil(t, err, gf.Identifier)

	// Make sure file is present and all metadata is set correctly.
	idAndUuid := fmt.Sprintf("%s (%s)", gf.Identifier, uuid)
	client.Head(uuid)
	require.Empty(t, client.ErrorMessage, idAndUuid)
	storedFile := client.StoredFile()
	require.NotNil(t, storedFile, idAndUuid)

	institution, _ := gf.InstitutionIdentifier()
	timeSinceLastModified := time.Since(storedFile.LastModified)
	assert.True(t, timeSinceLastModified < (10*time.Minute))
	assert.EqualValues(t, gf.Size, storedFile.Size, idAndUuid)
	assert.Equal(t, institution, storedFile.Institution, idAndUuid)
	assert.Equal(t, gf.IntellectualObjectIdentifier, storedFile.BagName, idAndUuid)
	assert.Equal(t, gf.OriginalPath(), storedFile.PathInBag, idAndUuid)
	assert.Equal(t, gf.GetChecksumByAlgorithm(constants.AlgMd5).Digest, storedFile.Md5, idAndUuid)
	assert.Equal(t, gf.GetChecksumByAlgorithm(constants.AlgSha256).Digest, storedFile.Sha256, idAndUuid)
}
<|endoftext|>


<commit_before>/*
Copyright IBM Corp All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/

package raft

import (
	"encoding/json"
	"testing"

	"github.com/hyperledger/fabric/integration"
	"github.com/hyperledger/fabric/integration/nwo"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

func TestRaft(t *testing.T) {
	RegisterFailHandler(Fail)
	RunSpecs(t, "Raft-based Ordering Service Suite")
}

var components *nwo.Components
var suiteBase = integration.RaftBasePort

var _ = SynchronizedBeforeSuite(func() []byte {
	components = &nwo.Components{}

	payload, err := json.Marshal(components)
	Expect(err).NotTo(HaveOccurred())

	return payload
}, func(payload []byte) {
	err := json.Unmarshal(payload, &components)
	Expect(err).NotTo(HaveOccurred())
})

var _ = SynchronizedAfterSuite(func() {
}, func() {
	components.Cleanup()
})

func StartPort() int {
	return suiteBase + (GinkgoParallelNode()-1)*100
}
<commit_msg>[FAB-15624] Reinstate RunQuery/RunInvoke functions<commit_after>/*
Copyright IBM Corp All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/

package raft

import (
	"encoding/json"
	"fmt"
	"testing"

	"github.com/hyperledger/fabric/integration"
	"github.com/hyperledger/fabric/integration/nwo"
	"github.com/hyperledger/fabric/integration/nwo/commands"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gbytes"
	"github.com/onsi/gomega/gexec"
)

func TestRaft(t *testing.T) {
	RegisterFailHandler(Fail)
	RunSpecs(t, "Raft-based Ordering Service Suite")
}

var components *nwo.Components
var suiteBase = integration.RaftBasePort

var _ = SynchronizedBeforeSuite(func() []byte {
	components = &nwo.Components{}

	payload, err := json.Marshal(components)
	Expect(err).NotTo(HaveOccurred())

	return payload
}, func(payload []byte) {
	err := json.Unmarshal(payload, &components)
	Expect(err).NotTo(HaveOccurred())
})

var _ = SynchronizedAfterSuite(func() {
}, func() {
	components.Cleanup()
})

func StartPort() int {
	return suiteBase + (GinkgoParallelNode()-1)*100
}

func RunInvoke(n *nwo.Network, orderer *nwo.Orderer, peer *nwo.Peer, channel string) {
	By("Querying chaincode")
	sess, err := n.PeerUserSession(peer, "User1", commands.ChaincodeInvoke{
		ChannelID: channel,
		Orderer:   n.OrdererAddress(orderer, nwo.ListenPort),
		Name:      "mycc",
		Ctor:      `{"Args":["invoke","a","b","10"]}`,
		PeerAddresses: []string{
			n.PeerAddress(n.Peer("Org1", "peer0"), nwo.ListenPort),
			n.PeerAddress(n.Peer("Org2", "peer1"), nwo.ListenPort),
		},
		WaitForEvent: true,
	})
	Expect(err).NotTo(HaveOccurred())
	Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0))
	Expect(sess.Err).To(gbytes.Say("Chaincode invoke successful. result: status:200"))
}

func RunQuery(n *nwo.Network, orderer *nwo.Orderer, peer *nwo.Peer, channel string) int {
	By("Invoking chaincode")
	sess, err := n.PeerUserSession(peer, "User1", commands.ChaincodeQuery{
		ChannelID: channel,
		Name:      "mycc",
		Ctor:      `{"Args":["query","a"]}`,
	})
	Expect(err).NotTo(HaveOccurred())
	Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0))

	var result int
	i, err := fmt.Sscanf(string(sess.Out.Contents()), "%d", &result)
	Expect(err).NotTo(HaveOccurred())
	Expect(i).To(Equal(1))
	return int(result)
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 Hardik Bagdi <hbagdi1@binghamton.edu>
//
// MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

package unsplash

import "bytes"

// ProfileImage contains URLs to profile image of a user
type ProfileImage struct {
	Small  *URL `json:"small"`
	Medium *URL `json:"medium"`
	Large  *URL `json:"large"`
	Custom *URL `json:"custom"`
}

// UserLinks contains URLs to
type UserLinks struct {
	Followers *URL `json:"followers"`
	Following *URL `json:"following"`
	Self      *URL `json:"self"`
	HTML      *URL `json:"html"`
	Photos    *URL `json:"photos"`
	Likes     *URL `json:"likes"`
	Portfolio *URL `json:"portfolio"`
}

// UserBadge contains information about badge for a user
type UserBadge struct {
	Title   *URL    `json:"title,omitempty"`
	Primary *bool   `json:"primary,omitempty"`
	Slug    *string `json:"slug,omitempty"`
	Link    *URL    `json:"link,omitempty"`
}

// User represents a Unsplash.com user
type User struct {
	UID                 *string       `json:"uid"`
	ID                  *string       `json:"id"`
	Username            *string       `json:"username"`
	Name                *string       `json:"name"`
	FirstName           *string       `json:"first_name"`
	CompletedOnboarding *bool         `json:"completed_onboarding"`
	LastName            *string       `json:"last_name,omitempty"`
	PortfolioURL        *URL          `json:"portfolio_url"`
	Bio                 *string       `json:"bio"`
	Location            *string       `json:"location"`
	TotalLikes          *int          `json:"total_likes"`
	TotalPhotos         *int          `json:"total_photos"`
	TotalCollections    *int          `json:"total_collections"`
	FollowedByUser      *bool         `json:"followed_by_user"`
	NumericID           *int          `json:"numeric_id"`
	FollowersCount      *int          `json:"followers_count"`
	FollowingCount      *int          `json:"following_count"`
	Downloads           *int          `json:"downloads"`
	ProfileImage        *ProfileImage `json:"profile_image"`
	Badge               *UserBadge    `json:"badge"`
	Links               *UserLinks    `json:"links,omitempty"`
	Photos              *[]Photo      `json:"photos"`
}

func (u User) String() string {
	var buf bytes.Buffer
	if u.ID == nil {
		return "User is not valid"
	}
	buf.WriteString("User: Name[")
	buf.WriteString(*u.Name)
	buf.WriteString("], ID[")
	buf.WriteString(*u.ID)
	buf.WriteString("]")
	return buf.String()
}
<commit_msg>minor fix for User String()<commit_after>// Copyright (c) 2017 Hardik Bagdi <hbagdi1@binghamton.edu>
//
// MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

package unsplash

import "bytes"

// ProfileImage contains URLs to profile image of a user
type ProfileImage struct {
	Small  *URL `json:"small"`
	Medium *URL `json:"medium"`
	Large  *URL `json:"large"`
	Custom *URL `json:"custom"`
}

// UserLinks contains URLs to
type UserLinks struct {
	Followers *URL `json:"followers"`
	Following *URL `json:"following"`
	Self      *URL `json:"self"`
	HTML      *URL `json:"html"`
	Photos    *URL `json:"photos"`
	Likes     *URL `json:"likes"`
	Portfolio *URL `json:"portfolio"`
}

// UserBadge contains information about badge for a user
type UserBadge struct {
	Title   *URL    `json:"title,omitempty"`
	Primary *bool   `json:"primary,omitempty"`
	Slug    *string `json:"slug,omitempty"`
	Link    *URL    `json:"link,omitempty"`
}

// User represents a Unsplash.com user
type User struct {
	UID                 *string       `json:"uid"`
	ID                  *string       `json:"id"`
	Username            *string       `json:"username"`
	Name                *string       `json:"name"`
	FirstName           *string       `json:"first_name"`
	CompletedOnboarding *bool         `json:"completed_onboarding"`
	LastName            *string       `json:"last_name,omitempty"`
	PortfolioURL        *URL          `json:"portfolio_url"`
	Bio                 *string       `json:"bio"`
	Location            *string       `json:"location"`
	TotalLikes          *int          `json:"total_likes"`
	TotalPhotos         *int          `json:"total_photos"`
	TotalCollections    *int          `json:"total_collections"`
	FollowedByUser      *bool         `json:"followed_by_user"`
	NumericID           *int          `json:"numeric_id"`
	FollowersCount      *int          `json:"followers_count"`
	FollowingCount      *int          `json:"following_count"`
	Downloads           *int          `json:"downloads"`
	ProfileImage        *ProfileImage `json:"profile_image"`
	Badge               *UserBadge    `json:"badge"`
	Links               *UserLinks    `json:"links,omitempty"`
	Photos              *[]Photo      `json:"photos"`
}

func (u *User) String() string {
	var buf bytes.Buffer
	if u.ID == nil {
		return "User is not valid"
	}
	buf.WriteString("User: Name[")
	buf.WriteString(*u.Name)
	buf.WriteString("], ID[")
	buf.WriteString(*u.ID)
	buf.WriteString("]")
	return buf.String()
}
<|endoftext|>


<commit_before>package genericsite

import (
	"errors"

	"github.com/xyproto/simpleredis"
	"github.com/xyproto/web"
)

type UserState struct {
	// see: http://redis.io/topics/data-types
	users       *simpleredis.HashMap        // Hash map of users, with several different fields per user ("loggedin", "confirmed", "email" etc)
	usernames   *simpleredis.Set            // A list of all usernames, for easy enumeration
	unconfirmed *simpleredis.Set            // A list of unconfirmed usernames, for easy enumeration
	pool        *simpleredis.ConnectionPool // A connection pool for Redis
}

// Also creates a new ConnectionPool
func NewUserState() *UserState {
	pool := simpleredis.NewConnectionPool()
	state := new(UserState)
	state.users = simpleredis.NewHashMap(pool, "users")
	state.usernames = simpleredis.NewSet(pool, "usernames")
	state.unconfirmed = simpleredis.NewSet(pool, "unconfirmed")
	state.pool = pool
	return state
}

func (state *UserState) GetPool() *simpleredis.ConnectionPool {
	return state.pool
}

func (state *UserState) Close() {
	state.pool.Close()
}

// Checks if the current user is logged in as a user right now
func (state *UserState) UserRights(ctx *web.Context) bool {
	if username := GetBrowserUsername(ctx); username != "" {
		return state.IsLoggedIn(username)
	}
	return false
}

func (state *UserState) HasUser(username string) bool {
	val, err := state.usernames.Has(username)
	if err != nil {
		// This happened at concurrent connections before introducing the connection pool
		panic("ERROR: Lost connection to Redis?")
	}
	return val
}

func (state *UserState) GetBooleanField(username, fieldname string) bool {
	hasUser := state.HasUser(username)
	if !hasUser {
		return false
	}
	chatting, err := state.users.Get(username, fieldname)
	if err != nil {
		return false
	}
	return TruthValue(chatting)
}

func (state *UserState) SetBooleanField(username, fieldname string, val bool) {
	strval := "false"
	if val {
		strval = "true"
	}
	state.users.Set(username, fieldname, strval)
}

func (state *UserState) IsConfirmed(username string) bool {
	return state.GetBooleanField(username, "confirmed")
}

// Checks if the given username is logged in or not
func (state *UserState) IsLoggedIn(username string) bool {
	if !state.HasUser(username) {
		return false
	}
	status, err := state.users.Get(username, "loggedin")
	if err != nil {
		// Returns "no" if the status can not be retrieved
		return false
	}
	return TruthValue(status)
}

// Checks if the current user is logged in as Administrator right now
func (state *UserState) AdminRights(ctx *web.Context) bool {
	if username := GetBrowserUsername(ctx); username != "" {
		return state.IsLoggedIn(username) && state.IsAdmin(username)
	}
	return false
}

// Checks if the given username is an administrator
func (state *UserState) IsAdmin(username string) bool {
	if !state.HasUser(username) {
		return false
	}
	status, err := state.users.Get(username, "admin")
	if err != nil {
		return false
	}
	return TruthValue(status)
}

// Gets the username that is stored in a cookie in the browser, if available
func GetBrowserUsername(ctx *web.Context) string {
	username, _ := ctx.GetSecureCookie("user")
	// TODO: Return err, then the calling function should notify the user that cookies are needed
	return username
}

func (state *UserState) SetBrowserUsername(ctx *web.Context, username string) error {
	if username == "" {
		return errors.New("Can't set cookie for empty username")
	}
	if !state.HasUser(username) {
		return errors.New("Can't store cookie for non-existsing user")
	}
	timeout := state.GetCookieTimeout(username)
	// Create a cookie that lasts for a while ("timeout" seconds),
	// this is the equivivalent of a session for a given username.
	ctx.SetSecureCookiePath("user", username, timeout, "/")
	return nil
}

func (state *UserState) GetAllUsernames() ([]string, error) {
	return state.usernames.GetAll()
}

func (state *UserState) GetEmail(username string) (string, error) {
	return state.users.Get(username, "email")
}

func (state *UserState) GetPasswordHash(username string) (string, error) {
	return state.users.Get(username, "password")
}

func (state *UserState) GetAllUnconfirmedUsernames() ([]string, error) {
	return state.unconfirmed.GetAll()
}

func (state *UserState) GetConfirmationCode(username string) (string, error) {
	return state.users.Get(username, "confirmationCode")
}

func (state *UserState) GetUsers() *simpleredis.HashMap {
	return state.users
}

// Add a user that has registered but not confirmed
func (state *UserState) AddUnconfirmed(username, confirmationCode string) {
	state.unconfirmed.Add(username)
	state.users.Set(username, "confirmationCode", confirmationCode)
}

// Remove a user that has registered but not confirmed
func (state *UserState) RemoveUnconfirmed(username string) {
	state.unconfirmed.Del(username)
	state.users.Del(username, "confirmationCode")
}

func (state *UserState) MarkConfirmed(username string) {
	state.users.Set(username, "confirmed", "true")
}

func (state *UserState) RemoveUser(username string) {
	state.usernames.Del(username)
	// Remove additional data as well
	state.users.Del(username, "loggedin")
}

func (state *UserState) SetAdminStatus(username string) {
	state.users.Set(username, "admin", "true")
}

func (state *UserState) RemoveAdminStatus(username string) {
	state.users.Set(username, "admin", "false")
}

// Creates a user without doing ANY checks
func (state *UserState) AddUserUnchecked(username, passwordHash, email string) {
	// Add the user
	state.usernames.Add(username)

	// Add password and email
	state.users.Set(username, "password", passwordHash)
	state.users.Set(username, "email", email)

	// Addditional fields
	additionalfields := []string{"loggedin", "confirmed", "admin"}
	for _, fieldname := range additionalfields {
		state.users.Set(username, fieldname, "false")
	}
}

func (state *UserState) SetLoggedIn(username string) {
	state.users.Set(username, "loggedin", "true")
}

func (state *UserState) SetLoggedOut(username string) {
	state.users.Set(username, "loggedin", "false")
}

// Get how long a login cookie should last
func (state *UserState) GetCookieTimeout(username string) int64 {
	// TODO: Store this in state.users
	return 3600
}
<commit_msg>Changed a method<commit_after>package genericsite

import (
	"errors"

	"github.com/xyproto/simpleredis"
	"github.com/xyproto/web"
)

const (
	LOGINCOOKIETIME = 3600 * 24 // The login cookie should last 24 hours
)

type UserState struct {
	// see: http://redis.io/topics/data-types
	users       *simpleredis.HashMap        // Hash map of users, with several different fields per user ("loggedin", "confirmed", "email" etc)
	usernames   *simpleredis.Set            // A list of all usernames, for easy enumeration
	unconfirmed *simpleredis.Set            // A list of unconfirmed usernames, for easy enumeration
	pool        *simpleredis.ConnectionPool // A connection pool for Redis
}

// Also creates a new ConnectionPool
func NewUserState() *UserState {
	pool := simpleredis.NewConnectionPool()
	state := new(UserState)
	state.users = simpleredis.NewHashMap(pool, "users")
	state.usernames = simpleredis.NewSet(pool, "usernames")
	state.unconfirmed = simpleredis.NewSet(pool, "unconfirmed")
	state.pool = pool
	return state
}

func (state *UserState) GetPool() *simpleredis.ConnectionPool {
	return state.pool
}

func (state *UserState) Close() {
	state.pool.Close()
}

// Checks if the current user is logged in as a user right now
func (state *UserState) UserRights(ctx *web.Context) bool {
	if username := GetBrowserUsername(ctx); username != "" {
		return state.IsLoggedIn(username)
	}
	return false
}

func (state *UserState) HasUser(username string) bool {
	val, err := state.usernames.Has(username)
	if err != nil {
		// This happened at concurrent connections before introducing the connection pool
		panic("ERROR: Lost connection to Redis?")
	}
	return val
}

func (state *UserState) GetBooleanField(username, fieldname string) bool {
	hasUser := state.HasUser(username)
	if !hasUser {
		return false
	}
	chatting, err := state.users.Get(username, fieldname)
	if err != nil {
		return false
	}
	return TruthValue(chatting)
}

func (state *UserState) SetBooleanField(username, fieldname string, val bool) {
	strval := "false"
	if val {
		strval = "true"
	}
	state.users.Set(username, fieldname, strval)
}

func (state *UserState) IsConfirmed(username string) bool {
	return state.GetBooleanField(username, "confirmed")
}

// Checks if the given username is logged in or not
func (state *UserState) IsLoggedIn(username string) bool {
	if !state.HasUser(username) {
		return false
	}
	status, err := state.users.Get(username, "loggedin")
	if err != nil {
		// Returns "no" if the status can not be retrieved
		return false
	}
	return TruthValue(status)
}

// Checks if the current user is logged in as Administrator right now
func (state *UserState) AdminRights(ctx *web.Context) bool {
	if username := GetBrowserUsername(ctx); username != "" {
		return state.IsLoggedIn(username) && state.IsAdmin(username)
	}
	return false
}

// Checks if the given username is an administrator
func (state *UserState) IsAdmin(username string) bool {
	if !state.HasUser(username) {
		return false
	}
	status, err := state.users.Get(username, "admin")
	if err != nil {
		return false
	}
	return TruthValue(status)
}

// Gets the username that is stored in a cookie in the browser, if available
func GetBrowserUsername(ctx *web.Context) string {
	username, _ := ctx.GetSecureCookie("user")
	// TODO: Return err, then the calling function should notify the user that cookies are needed
	return username
}

func (state *UserState) SetBrowserUsername(ctx *web.Context, username string) error {
	if username == "" {
		return errors.New("Can't set cookie for empty username")
	}
	if !state.HasUser(username) {
		return errors.New("Can't store cookie for non-existsing user")
	}
	timeout := state.GetCookieTimeout(username)
	// Create a cookie that lasts for a while ("timeout" seconds),
	// this is the equivivalent of a session for a given username.
	ctx.SetSecureCookiePath("user", username, timeout, "/")
	return nil
}

func (state *UserState) GetAllUsernames() ([]string, error) {
	return state.usernames.GetAll()
}

func (state *UserState) GetEmail(username string) (string, error) {
	return state.users.Get(username, "email")
}

func (state *UserState) GetPasswordHash(username string) (string, error) {
	return state.users.Get(username, "password")
}

func (state *UserState) GetAllUnconfirmedUsernames() ([]string, error) {
	return state.unconfirmed.GetAll()
}

func (state *UserState) GetConfirmationCode(username string) (string, error) {
	return state.users.Get(username, "confirmationCode")
}

func (state *UserState) GetUsers() *simpleredis.HashMap {
	return state.users
}

// Add a user that has registered but not confirmed
func (state *UserState) AddUnconfirmed(username, confirmationCode string) {
	state.unconfirmed.Add(username)
	state.users.Set(username, "confirmationCode", confirmationCode)
}

// Remove a user that has registered but not confirmed
func (state *UserState) RemoveUnconfirmed(username string) {
	state.unconfirmed.Del(username)
	state.users.DelKey(username, "confirmationCode")
}

func (state *UserState) MarkConfirmed(username string) {
	state.users.Set(username, "confirmed", "true")
}

func (state *UserState) RemoveUser(username string) {
	state.usernames.Del(username)
	// Remove additional data as well
	state.users.DelKey(username, "loggedin")
}

func (state *UserState) SetAdminStatus(username string) {
	state.users.Set(username, "admin", "true")
}

func (state *UserState) RemoveAdminStatus(username string) {
	state.users.Set(username, "admin", "false")
}

// Creates a user without doing ANY checks
func (state *UserState) AddUserUnchecked(username, passwordHash, email string) {
	// Add the user
	state.usernames.Add(username)

	// Add password and email
	state.users.Set(username, "password", passwordHash)
	state.users.Set(username, "email", email)

	// Addditional fields
	additionalfields := []string{"loggedin", "confirmed", "admin"}
	for _, fieldname := range additionalfields {
		state.users.Set(username, fieldname, "false")
	}
}

func (state *UserState) SetLoggedIn(username string) {
	state.users.Set(username, "loggedin", "true")
}

func (state *UserState) SetLoggedOut(username string) {
	state.users.Set(username, "loggedin", "false")
}

// Get how long a login cookie should last
func (state *UserState) GetCookieTimeout(username string) int64 {
	// TODO: Store this in state.users
	return LOGINCOOKIETIME
}
<|endoftext|>


<commit_before>package util

import (
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/csv"
	"encoding/pem"
	"fmt"
	"io"
	"io/ioutil"
	"math/big"
	"net/http"
	"net/mail"
	"os"
	"regexp"
	"time"

	log "github.com/gophish/gophish/logger"
	"github.com/gophish/gophish/models"
	"github.com/jordan-wright/email"
)

var (
	firstNameRegex = regexp.MustCompile(`(?i)first[\s_-]*name`)
	lastNameRegex  = regexp.MustCompile(`(?i)last[\s_-]*name`)
	emailRegex     = regexp.MustCompile(`(?i)email`)
	positionRegex  = regexp.MustCompile(`(?i)position`)
)

// ParseMail takes in an HTTP Request and returns an Email object
// TODO: This function will likely be changed to take in a []byte
func ParseMail(r *http.Request) (email.Email, error) {
	e := email.Email{}
	m, err := mail.ReadMessage(r.Body)
	if err != nil {
		fmt.Println(err)
	}
	body, err := ioutil.ReadAll(m.Body)
	e.HTML = body
	return e, err
}

// ParseCSV contains the logic to parse the user provided csv file containing Target entries
func ParseCSV(r *http.Request) ([]models.Target, error) {
	mr, err := r.MultipartReader()
	ts := []models.Target{}
	if err != nil {
		return ts, err
	}
	for {
		part, err := mr.NextPart()
		if err == io.EOF {
			break
		}
		// Skip the "submit" part
		if part.FileName() == "" {
			continue
		}
		defer part.Close()
		reader := csv.NewReader(part)
		reader.TrimLeadingSpace = true
		record, err := reader.Read()
		if err == io.EOF {
			break
		}
		fi := -1
		li := -1
		ei := -1
		pi := -1
		fn := ""
		ln := ""
		ea := ""
		ps := ""
		for i, v := range record {
			switch {
			case firstNameRegex.MatchString(v):
				fi = i
			case lastNameRegex.MatchString(v):
				li = i
			case emailRegex.MatchString(v):
				ei = i
			case positionRegex.MatchString(v):
				pi = i
			}
		}
		for {
			record, err := reader.Read()
			if err == io.EOF {
				break
			}
			if fi != -1 && len(record) > fi {
				fn = record[fi]
			}
			if li != -1 && len(record) > li {
				ln = record[li]
			}
			if ei != -1 && len(record) > ei {
				csvEmail, err := mail.ParseAddress(record[ei])
				if err != nil {
					continue
				}
				ea = csvEmail.Address
			}
			if pi != -1 && len(record) > pi {
				ps = record[pi]
			}
			t := models.Target{
				BaseRecipient: models.BaseRecipient{
					FirstName: fn,
					LastName:  ln,
					Email:     ea,
					Position:  ps,
				},
			}
			ts = append(ts, t)
		}
	}
	return ts, nil
}

// CheckAndCreateSSL is a helper to setup self-signed certificates for the administrative interface.
func CheckAndCreateSSL(cp string, kp string) error {
	// Check whether there is an existing SSL certificate and/or key, and if so, abort execution of this function
	if _, err := os.Stat(cp); !os.IsNotExist(err) {
		return nil
	}
	if _, err := os.Stat(kp); !os.IsNotExist(err) {
		return nil
	}

	log.Infof("Creating new self-signed certificates for administration interface")

	priv, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)

	notBefore := time.Now()
	// Generate a certificate that lasts for 10 years
	notAfter := notBefore.Add(10 * 365 * 24 * time.Hour)

	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
	serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)

	if err != nil {
		return fmt.Errorf("TLS Certificate Generation: Failed to generate a random serial number: %s", err)
	}

	template := x509.Certificate{
		SerialNumber: serialNumber,
		Subject: pkix.Name{
			Organization: []string{"Gophish"},
		},
		NotBefore: notBefore,
		NotAfter:  notAfter,

		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
		BasicConstraintsValid: true,
	}

	derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, priv.Public(), priv)
	if err != nil {
		return fmt.Errorf("TLS Certificate Generation: Failed to create certificate: %s", err)
	}

	certOut, err := os.Create(cp)
	if err != nil {
		return fmt.Errorf("TLS Certificate Generation: Failed to open %s for writing: %s", cp, err)
	}
	pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
	certOut.Close()

	keyOut, err := os.OpenFile(kp, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
	if err != nil {
		return fmt.Errorf("TLS Certificate Generation: Failed to open %s for writing", kp)
	}

	b, err := x509.MarshalECPrivateKey(priv)
	if err != nil {
		return fmt.Errorf("TLS Certificate Generation: Unable to marshal ECDSA private key: %v", err)
	}

	pem.Encode(keyOut, &pem.Block{Type: "EC PRIVATE KEY", Bytes: b})
	keyOut.Close()

	log.Info("TLS Certificate Generation complete")
	return nil
}
<commit_msg>Prevent adding an empty recipient (#1448)<commit_after>package util

import (
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/csv"
	"encoding/pem"
	"fmt"
	"io"
	"io/ioutil"
	"math/big"
	"net/http"
	"net/mail"
	"os"
	"regexp"
	"time"

	log "github.com/gophish/gophish/logger"
	"github.com/gophish/gophish/models"
	"github.com/jordan-wright/email"
)

var (
	firstNameRegex = regexp.MustCompile(`(?i)first[\s_-]*name`)
	lastNameRegex  = regexp.MustCompile(`(?i)last[\s_-]*name`)
	emailRegex     = regexp.MustCompile(`(?i)email`)
	positionRegex  = regexp.MustCompile(`(?i)position`)
)

// ParseMail takes in an HTTP Request and returns an Email object
// TODO: This function will likely be changed to take in a []byte
func ParseMail(r *http.Request) (email.Email, error) {
	e := email.Email{}
	m, err := mail.ReadMessage(r.Body)
	if err != nil {
		fmt.Println(err)
	}
	body, err := ioutil.ReadAll(m.Body)
	e.HTML = body
	return e, err
}

// ParseCSV contains the logic to parse the user provided csv file containing Target entries
func ParseCSV(r *http.Request) ([]models.Target, error) {
	mr, err := r.MultipartReader()
	ts := []models.Target{}
	if err != nil {
		return ts, err
	}
	for {
		part, err := mr.NextPart()
		if err == io.EOF {
			break
		}
		// Skip the "submit" part
		if part.FileName() == "" {
			continue
		}
		defer part.Close()
		reader := csv.NewReader(part)
		reader.TrimLeadingSpace = true
		record, err := reader.Read()
		if err == io.EOF {
			break
		}
		fi := -1
		li := -1
		ei := -1
		pi := -1
		fn := ""
		ln := ""
		ea := ""
		ps := ""
		for i, v := range record {
			switch {
			case firstNameRegex.MatchString(v):
				fi = i
			case lastNameRegex.MatchString(v):
				li = i
			case emailRegex.MatchString(v):
				ei = i
			case positionRegex.MatchString(v):
				pi = i
			}
		}
		if fi == -1 && li == -1 && ei == -1 && pi == -1 {
			continue
		}
		for {
			record, err := reader.Read()
			if err == io.EOF {
				break
			}
			if fi != -1 && len(record) > fi {
				fn = record[fi]
			}
			if li != -1 && len(record) > li {
				ln = record[li]
			}
			if ei != -1 && len(record) > ei {
				csvEmail, err := mail.ParseAddress(record[ei])
				if err != nil {
					continue
				}
				ea = csvEmail.Address
			}
			if pi != -1 && len(record) > pi {
				ps = record[pi]
			}
			t := models.Target{
				BaseRecipient: models.BaseRecipient{
					FirstName: fn,
					LastName:  ln,
					Email:     ea,
					Position:  ps,
				},
			}
			ts = append(ts, t)
		}
	}
	return ts, nil
}

// CheckAndCreateSSL is a helper to setup self-signed certificates for the administrative interface.
func CheckAndCreateSSL(cp string, kp string) error {
	// Check whether there is an existing SSL certificate and/or key, and if so, abort execution of this function
	if _, err := os.Stat(cp); !os.IsNotExist(err) {
		return nil
	}
	if _, err := os.Stat(kp); !os.IsNotExist(err) {
		return nil
	}

	log.Infof("Creating new self-signed certificates for administration interface")

	priv, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)

	notBefore := time.Now()
	// Generate a certificate that lasts for 10 years
	notAfter := notBefore.Add(10 * 365 * 24 * time.Hour)

	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
	serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)

	if err != nil {
		return fmt.Errorf("TLS Certificate Generation: Failed to generate a random serial number: %s", err)
	}

	template := x509.Certificate{
		SerialNumber: serialNumber,
		Subject: pkix.Name{
			Organization: []string{"Gophish"},
		},
		NotBefore: notBefore,
		NotAfter:  notAfter,

		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
		BasicConstraintsValid: true,
	}

	derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, priv.Public(), priv)
	if err != nil {
		return fmt.Errorf("TLS Certificate Generation: Failed to create certificate: %s", err)
	}

	certOut, err := os.Create(cp)
	if err != nil {
		return fmt.Errorf("TLS Certificate Generation: Failed to open %s for writing: %s", cp, err)
	}
	pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
	certOut.Close()

	keyOut, err := os.OpenFile(kp, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
	if err != nil {
		return fmt.Errorf("TLS Certificate Generation: Failed to open %s for writing", kp)
	}

	b, err := x509.MarshalECPrivateKey(priv)
	if err != nil {
		return fmt.Errorf("TLS Certificate Generation: Unable to marshal ECDSA private key: %v", err)
	}

	pem.Encode(keyOut, &pem.Block{Type: "EC PRIVATE KEY", Bytes: b})
	keyOut.Close()

	log.Info("TLS Certificate Generation complete")
	return nil
}
<|endoftext|>


<commit_before>package gomon

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

var _ = Describe("Util", func() {

	Describe("calculateStats", func() {
		var slice []float64

		Context("called with a slice of all zeros", func() {
			slice = []float64{0.0}
			It("should return the appropriate stats", func() {
				allZero, min, max, sum := calculateStats(slice)
				Expect(allZero).To(BeTrue())
				Expect(min).To(BeZero())
				Expect(max).To(BeZero())
				Expect(sum).To(BeZero())
			})
		})

		Context("with an empty slice", func() {
			It("should panic", func() {
				slice = []float64{}
				Expect(func() { calculateStats(slice) }).To(Panic())
			})
		})

	})

	Describe("deltaSinceLastCall", func() {
		var (
			emitter func() float64
			subject func() float64
		)

		Context("with a function that changes", func() {
			It("reflects the changes in successive calls", func() {
				emitter = Fib()
				subject = DeltaSinceLastCall(emitter)
				Expect(subject()).To(Equal(0.0))
				Expect(subject()).To(Equal(1.0))
				Expect(subject()).To(Equal(1.0))
				Expect(subject()).To(Equal(2.0))
			})
		})

		Context("with a function that does not change", func() {
			It("always shows zero delta", func() {
				emitter = func() float64 { return 1.0 }
				subject = DeltaSinceLastCall(emitter)
				Expect(subject()).To(Equal(0.0))
				Expect(subject()).To(Equal(0.0))
				Expect(subject()).To(Equal(0.0))
			})
		})

	})

})

// When called successively, returns the next term in the Fibonacci sequence.
func Fib() (generatorFunc func() float64) {
	a, b := 0.0, 1.0
	generatorFunc = func() float64 {
		a, b = b, a+b
		return a
	}
	return
}
<commit_msg>Add a test for normal usage of calculateStats<commit_after>package gomon

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

var _ = Describe("Util", func() {

	Describe("calculateStats", func() {
		var slice []float64

		Context("called with a slice of all zeros", func() {
			BeforeEach(func() { slice = []float64{0.0} })

			It("should return the appropriate stats", func() {
				allZero, min, max, sum := calculateStats(slice)
				Expect(allZero).To(BeTrue())
				Expect(min).To(BeZero())
				Expect(max).To(BeZero())
				Expect(sum).To(BeZero())
			})

		})

		Context("with an empty slice", func() {
			BeforeEach(func() { slice = []float64{} })

			It("should panic", func() {
				Expect(func() { calculateStats(slice) }).To(Panic())
			})

		})

		Context("with some sample data points", func() {
			BeforeEach(func() { slice = []float64{1.0, 2.0, 3.0} })

			It("should return the appropriate stats", func() {
				allZero, min, max, sum := calculateStats(slice)
				Expect(allZero).To(BeFalse())
				Expect(min).To(Equal(1.0))
				Expect(max).To(Equal(3.0))
				Expect(sum).To(Equal(6.0))
			})

		})

	})

	Describe("deltaSinceLastCall", func() {
		var (
			emitter func() float64
			subject func() float64
		)

		Context("with a function that changes", func() {
			It("reflects the changes in successive calls", func() {
				emitter = Fib()
				subject = DeltaSinceLastCall(emitter)
				Expect(subject()).To(Equal(0.0))
				Expect(subject()).To(Equal(1.0))
				Expect(subject()).To(Equal(1.0))
				Expect(subject()).To(Equal(2.0))
			})
		})

		Context("with a function that does not change", func() {
			It("always shows zero delta", func() {
				emitter = func() float64 { return 1.0 }
				subject = DeltaSinceLastCall(emitter)
				Expect(subject()).To(Equal(0.0))
				Expect(subject()).To(Equal(0.0))
				Expect(subject()).To(Equal(0.0))
			})
		})

	})

})

// When called successively, returns the next term in the Fibonacci sequence.
func Fib() (generatorFunc func() float64) {
	a, b := 0.0, 1.0
	generatorFunc = func() float64 {
		a, b = b, a+b
		return a
	}
	return
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	. "github.com/smartystreets/goconvey/convey"
	"os"
	"strings"
	"testing"
	"time"
)

type testWriteCloser struct {
	failOnWrite  bool
	bytesWritten int
	closed       bool
}

func (twc *testWriteCloser) Write(value []byte) (int, error) {
	if twc.failOnWrite {
		return 0, errors.New("Some error")
	}
	twc.bytesWritten = twc.bytesWritten + len(value)
	return len(value), nil
}

func (twc *testWriteCloser) Close() error {
	if twc.closed {
		return errors.New("double close")
	}
	twc.closed = true
	return nil
}

type testFDBFileInfo struct {
	rev int
}

func (tInfo *testFDBFileInfo) Name() string {
	return fmt.Sprintf("%08d", tInfo.rev)
}
func (tInfo *testFDBFileInfo) Size() int64 { return 0 }
func (tInfo *testFDBFileInfo) Mode() os.FileMode {
	return os.FileMode(0750)
}
func (tInfo *testFDBFileInfo) ModTime() time.Time {
	return time.Now()
}
func (tInfo *testFDBFileInfo) IsDir() bool      { return false }
func (tInfo *testFDBFileInfo) Sys() interface{} { return nil }

func TestWikiWordRe(t *testing.T) {
	Convey("All WikiWords match a regexp", t, func() {
		So(IsWikiWord("abcd"), ShouldBeFalse)
		So(IsWikiWord("123Abc"), ShouldBeFalse)
		So(IsWikiWord("abcDef"), ShouldBeFalse)
		So(IsWikiWord("Abc+d3"), ShouldBeFalse)
		So(IsWikiWord("Abc"), ShouldBeFalse)
		So(IsWikiWord("Jonathan@Hanks"), ShouldBeFalse)
		So(IsWikiWord("FirstLast"), ShouldBeTrue)
		So(IsWikiWord("FirstLast123"), ShouldBeTrue)
		So(IsWikiWord("FirstLastOther"), ShouldBeTrue)
	})
}

func TestExtractWikiWords(t *testing.T) {
	test0 := []byte("There are no wiki words in\nthis piece of text.")
	test1 := []byte("There is only\nOneWikiWord in this text.")
	test4 := []byte("OneWikiWord not a wiki word\nTwoWikiWord ThreeWikiWords\n FourWords")
	testMulti := []byte("WordWordOne\nWordWord\n WordOne\nWordWordOne WordWordOne")

	Convey("A string/[]byte may have multiple wikiwords in it", t, func() {
		words0 := ExtractWikiWords(test0)
		So(len(words0), ShouldEqual, 0)

		words1 := ExtractWikiWords(test1)
		So(len(words1), ShouldEqual, 1)

		words4 := ExtractWikiWords(test4)
		So(len(words4), ShouldEqual, 4)

		wordsMulti := ExtractWikiWords(testMulti)
		So(len(wordsMulti), ShouldEqual, 3)
	})
}

func TestWriteAndClose(t *testing.T) {
	Convey("writeAndClose should always call close no matter what happens", t, func() {
		twc1 := testWriteCloser{}
		err := writeAndClose(&twc1, []byte("1234"))
		So(err, ShouldBeNil)
		So(twc1.closed, ShouldBeTrue)
		twc2 := testWriteCloser{failOnWrite: true}
		err = writeAndClose(&twc2, []byte("1234"))
		So(err, ShouldNotBeNil)
		So(twc2.closed, ShouldBeTrue)
	})
}

func TestCopyAndClose(t *testing.T) {
	Convey("copyAndClose should always call close no matter what happens", t, func() {
		twc1 := testWriteCloser{}
		err := copyAndClose(&twc1, strings.NewReader("1234"))
		So(err, ShouldBeNil)
		So(twc1.closed, ShouldBeTrue)
		twc2 := testWriteCloser{failOnWrite: true}
		err = copyAndClose(&twc2, strings.NewReader("1234"))
		So(err, ShouldNotBeNil)
		So(twc2.closed, ShouldBeTrue)
	})
}

func TestGetMaxFDBRevision(t *testing.T) {
	Convey("getMaxFDBRevision should return the highest file revision number for a list of os.FileInfos, where the format of the filename is the regexp [0-9]{8}", t, func() {
		fInfos := make([]os.FileInfo, 0, 3)
		fInfos = append(fInfos, &testFDBFileInfo{rev: 0})
		fInfos = append(fInfos, &testFDBFileInfo{rev: 5})
		fInfos = append(fInfos, &testFDBFileInfo{rev: 3})
		So(getMaxFDBRevision(fInfos), ShouldEqual, 5)
		So(getMaxFDBRevision(make([]os.FileInfo, 0, 0)), ShouldEqual, -1)
	})
}

func TestGetCountFDBAttachments(t *testing.T) {
	Convey("getCountFDBAttachments should return the number of attachments found by matching names to the attachment regexp", t, func() {
		fInfos := make([]os.FileInfo, 0, 3)
		fInfos = append(fInfos, &testFDBFileInfo{rev: 0})
		fInfos = append(fInfos, &testFDBFileInfo{rev: 5})
		fInfos = append(fInfos, &testFDBFileInfo{rev: 3})
		So(getMaxFDBRevision(fInfos), ShouldEqual, 5)
		So(getMaxFDBRevision(make([]os.FileInfo, 0, 0)), ShouldEqual, -1)
	})
}

func TestGenerateRevisionSplit(t *testing.T) {
	tests := []struct {
		InCur, InMax, OutCur, OutMin, OutMax int
	}{
		{-1, 5, 5, 0, 5},
		{5, 5, 5, 0, 5},
		{-1, 10, 10, 5, 10},
		{10, 10, 10, 5, 10},
		{5, 20, 5, 0, 10},
		{-1, 0, 0, 0, 0},
	}

	for _, testVal := range tests {
		cur, min, max := generateRevisionSplit(testVal.InCur, testVal.InMax)
		if testVal.OutCur != cur || testVal.OutMin != min || testVal.OutMax != max {
			t.Errorf("inputs (%d, %d) outputs (%d, %d, %d) expecting (%d, %d, %d)", testVal.InCur, testVal.InMax, cur, min, max, testVal.OutCur, testVal.OutMin, testVal.OutMax)
		}
	}
}

func TestGenerateInt(t *testing.T) {
	var v int

	for v = range generateInt(0, 5) {
	}
	if v != 5 {
		t.Error("Expecting 5, got ", v)
	}
	for v = range generateInt(5, 0) {
	}
	if v != 0 {
		t.Error("Expecting 0, got ", v)
	}
}
<commit_msg>Additional WikiWord tests<commit_after>package main

import (
	"errors"
	"fmt"
	. "github.com/smartystreets/goconvey/convey"
	"os"
	"strings"
	"testing"
	"time"
)

type testWriteCloser struct {
	failOnWrite  bool
	bytesWritten int
	closed       bool
}

func (twc *testWriteCloser) Write(value []byte) (int, error) {
	if twc.failOnWrite {
		return 0, errors.New("Some error")
	}
	twc.bytesWritten = twc.bytesWritten + len(value)
	return len(value), nil
}

func (twc *testWriteCloser) Close() error {
	if twc.closed {
		return errors.New("double close")
	}
	twc.closed = true
	return nil
}

type testFDBFileInfo struct {
	rev int
}

func (tInfo *testFDBFileInfo) Name() string {
	return fmt.Sprintf("%08d", tInfo.rev)
}
func (tInfo *testFDBFileInfo) Size() int64 { return 0 }
func (tInfo *testFDBFileInfo) Mode() os.FileMode {
	return os.FileMode(0750)
}
func (tInfo *testFDBFileInfo) ModTime() time.Time {
	return time.Now()
}
func (tInfo *testFDBFileInfo) IsDir() bool      { return false }
func (tInfo *testFDBFileInfo) Sys() interface{} { return nil }

func TestWikiWordRe(t *testing.T) {
	Convey("All WikiWords match a regexp", t, func() {
		So(IsWikiWord("abcd"), ShouldBeFalse)
		So(IsWikiWord("123"), ShouldBeFalse)
		So(IsWikiWord("123Abc"), ShouldBeFalse)
		So(IsWikiWord("abcDef"), ShouldBeFalse)
		So(IsWikiWord("Abc+d3"), ShouldBeFalse)
		So(IsWikiWord("Abc_Def"), ShouldBeTrue)
		So(IsWikiWord("AbcD_ef"), ShouldBeTrue)
		So(IsWikiWord("AbcD_123"), ShouldBeTrue)
		So(IsWikiWord("Abc"), ShouldBeFalse)
		So(IsWikiWord("Jonathan@Hanks"), ShouldBeFalse)
		So(IsWikiWord("FirstLast"), ShouldBeTrue)
		So(IsWikiWord("FirstLast123"), ShouldBeTrue)
		So(IsWikiWord("FirstLastOther"), ShouldBeTrue)
	})
}

func TestExtractWikiWords(t *testing.T) {
	test0 := []byte("There are no wiki words in\nthis piece of text.")
	test1 := []byte("There is only\nOneWikiWord in this text.")
	test4 := []byte("OneWikiWord not a wiki word\nTwoWikiWord ThreeWikiWords\n FourWords")
	testMulti := []byte("WordWordOne\nWordWord\n WordOne\nWordWordOne WordWordOne")

	Convey("A string/[]byte may have multiple wikiwords in it", t, func() {
		words0 := ExtractWikiWords(test0)
		So(len(words0), ShouldEqual, 0)

		words1 := ExtractWikiWords(test1)
		So(len(words1), ShouldEqual, 1)

		words4 := ExtractWikiWords(test4)
		So(len(words4), ShouldEqual, 4)

		wordsMulti := ExtractWikiWords(testMulti)
		So(len(wordsMulti), ShouldEqual, 3)
	})
}

func TestWriteAndClose(t *testing.T) {
	Convey("writeAndClose should always call close no matter what happens", t, func() {
		twc1 := testWriteCloser{}
		err := writeAndClose(&twc1, []byte("1234"))
		So(err, ShouldBeNil)
		So(twc1.closed, ShouldBeTrue)
		twc2 := testWriteCloser{failOnWrite: true}
		err = writeAndClose(&twc2, []byte("1234"))
		So(err, ShouldNotBeNil)
		So(twc2.closed, ShouldBeTrue)
	})
}

func TestCopyAndClose(t *testing.T) {
	Convey("copyAndClose should always call close no matter what happens", t, func() {
		twc1 := testWriteCloser{}
		err := copyAndClose(&twc1, strings.NewReader("1234"))
		So(err, ShouldBeNil)
		So(twc1.closed, ShouldBeTrue)
		twc2 := testWriteCloser{failOnWrite: true}
		err = copyAndClose(&twc2, strings.NewReader("1234"))
		So(err, ShouldNotBeNil)
		So(twc2.closed, ShouldBeTrue)
	})
}

func TestGetMaxFDBRevision(t *testing.T) {
	Convey("getMaxFDBRevision should return the highest file revision number for a list of os.FileInfos, where the format of the filename is the regexp [0-9]{8}", t, func() {
		fInfos := make([]os.FileInfo, 0, 3)
		fInfos = append(fInfos, &testFDBFileInfo{rev: 0})
		fInfos = append(fInfos, &testFDBFileInfo{rev: 5})
		fInfos = append(fInfos, &testFDBFileInfo{rev: 3})
		So(getMaxFDBRevision(fInfos), ShouldEqual, 5)
		So(getMaxFDBRevision(make([]os.FileInfo, 0, 0)), ShouldEqual, -1)
	})
}

func TestGetCountFDBAttachments(t *testing.T) {
	Convey("getCountFDBAttachments should return the number of attachments found by matching names to the attachment regexp", t, func() {
		fInfos := make([]os.FileInfo, 0, 3)
		fInfos = append(fInfos, &testFDBFileInfo{rev: 0})
		fInfos = append(fInfos, &testFDBFileInfo{rev: 5})
		fInfos = append(fInfos, &testFDBFileInfo{rev: 3})
		So(getMaxFDBRevision(fInfos), ShouldEqual, 5)
		So(getMaxFDBRevision(make([]os.FileInfo, 0, 0)), ShouldEqual, -1)
	})
}

func TestGenerateRevisionSplit(t *testing.T) {
	tests := []struct {
		InCur, InMax, OutCur, OutMin, OutMax int
	}{
		{-1, 5, 5, 0, 5},
		{5, 5, 5, 0, 5},
		{-1, 10, 10, 5, 10},
		{10, 10, 10, 5, 10},
		{5, 20, 5, 0, 10},
		{-1, 0, 0, 0, 0},
	}

	for _, testVal := range tests {
		cur, min, max := generateRevisionSplit(testVal.InCur, testVal.InMax)
		if testVal.OutCur != cur || testVal.OutMin != min || testVal.OutMax != max {
			t.Errorf("inputs (%d, %d) outputs (%d, %d, %d) expecting (%d, %d, %d)", testVal.InCur, testVal.InMax, cur, min, max, testVal.OutCur, testVal.OutMin, testVal.OutMax)
		}
	}
}

func TestGenerateInt(t *testing.T) {
	var v int

	for v = range generateInt(0, 5) {
	}
	if v != 5 {
		t.Error("Expecting 5, got ", v)
	}
	for v = range generateInt(5, 0) {
	}
	if v != 0 {
		t.Error("Expecting 0, got ", v)
	}
}
<|endoftext|>


<commit_before>package bagman_test

import (
    "testing"
    "os"
    "github.com/APTrust/bagman"
)

func TestBagmanHome(t *testing.T) {
    bagmanHome := os.Getenv("BAGMAN_HOME")
    goHome := os.Getenv("GO_HOME")

    // Should use BAGMAN_HOME, if it's set...
    os.Setenv("BAGMAN_HOME", "/bagman_home")
    defer os.Setenv("BAGMAN_HOME", bagmanHome)
    bagmanHome, err := bagman.BagmanHome()
    if err != nil {
        t.Error(err)
    }
    if bagmanHome != "/bagman_home" {
        t.Errorf("BagmanHome returned '%s', expected '%s'",
            bagmanHome,
            "/bagman_home")
    }
    os.Setenv("BAGMAN_HOME", "")

    // Otherwise, should use GO_HOME
    os.Setenv("GO_HOME", "/go_home")
    defer os.Setenv("GO_HOME", goHome)
    bagmanHome, err = bagman.BagmanHome()
    if err != nil {
        t.Error(err)
    }
    if bagmanHome != "/go_home/src/github.com/APTrust/bagman" {
        t.Errorf("BagmanHome returned '%s', expected '%s'",
            bagmanHome,
            "/go_home")
    }
    os.Setenv("GO_HOME", "")

    bagmanHome, err = bagman.BagmanHome()
    if err == nil {
        t.Error("BagmanHome should have an thrown exception.")
    }

}
<commit_msg>Added test for LoadRelativeFile<commit_after>package bagman_test

import (
    "testing"
    "os"
    "path/filepath"
    "github.com/APTrust/bagman"
)

func TestBagmanHome(t *testing.T) {
    bagmanHome := os.Getenv("BAGMAN_HOME")
    goHome := os.Getenv("GO_HOME")
    defer os.Setenv("BAGMAN_HOME", bagmanHome)
    defer os.Setenv("GO_HOME", goHome)

    // Should use BAGMAN_HOME, if it's set...
    os.Setenv("BAGMAN_HOME", "/bagman_home")
    bagmanHome, err := bagman.BagmanHome()
    if err != nil {
        t.Error(err)
    }
    if bagmanHome != "/bagman_home" {
        t.Errorf("BagmanHome returned '%s', expected '%s'",
            bagmanHome,
            "/bagman_home")
    }
    os.Setenv("BAGMAN_HOME", "")

    // Otherwise, should use GO_HOME
    os.Setenv("GO_HOME", "/go_home")
    bagmanHome, err = bagman.BagmanHome()
    if err != nil {
        t.Error(err)
    }
    if bagmanHome != "/go_home/src/github.com/APTrust/bagman" {
        t.Errorf("BagmanHome returned '%s', expected '%s'",
            bagmanHome,
            "/go_home")
    }
    os.Setenv("GO_HOME", "")

    // Without BAGMAN_HOME and GO_HOME, we should get an error
    bagmanHome, err = bagman.BagmanHome()
    if err == nil {
        t.Error("BagmanHome should have an thrown exception.")
    }
}

func TestLoadRelativeFile(t *testing.T) {
    path := filepath.Join("testdata", "result_good.json")
    data, err := bagman.LoadRelativeFile(path)
    if err != nil {
        t.Error(err)
    }
    if data == nil || len(data) == 0 {
        t.Errorf("Read no data out of file '%s'", path)
    }
}
<|endoftext|>


<commit_before>// Copyright 2013 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.

package utils_test

import (
	"github.com/juju/testing"
	jc "github.com/juju/testing/checkers"
	gc "gopkg.in/check.v1"

	"github.com/juju/utils"
)

type uuidSuite struct {
	testing.IsolationSuite
}

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

func (*uuidSuite) TestUUID(c *gc.C) {
	uuid, err := utils.NewUUID()
	c.Assert(err, gc.IsNil)
	uuidCopy := uuid.Copy()
	uuidRaw := uuid.Raw()
	uuidStr := uuid.String()
	c.Assert(uuidRaw, gc.HasLen, 16)
	c.Assert(uuidStr, jc.Satisfies, utils.IsValidUUIDString)
	uuid[0] = 0x00
	uuidCopy[0] = 0xFF
	c.Assert(uuid, gc.Not(gc.DeepEquals), uuidCopy)
	uuidRaw[0] = 0xFF
	c.Assert(uuid, gc.Not(gc.DeepEquals), uuidRaw)
	nextUUID, err := utils.NewUUID()
	c.Assert(err, gc.IsNil)
	c.Assert(uuid, gc.Not(gc.DeepEquals), nextUUID)
}

func (*uuidSuite) TestIsValidUUIDFailsWhenNotValid(c *gc.C) {
	c.Check(utils.IsValidUUIDString("blah"), gc.Equals, false)
	c.Check(utils.IsValidUUIDString("blah-9f484882-2f18-4fd2-967d-db9663db7bea"), gc.Equals, false)
	c.Check(utils.IsValidUUIDString("9f484882-2f18-4fd2-967d-db9663db7bea-blah"), gc.Equals, false)
	c.Check(utils.IsValidUUIDString("9f484882-2f18-4fd2-967d-db9663db7bea"), gc.Equals, true)
}

func (*uuidSuite) TestUUIDFromString(c *gc.C) {
	_, err := utils.UUIDFromString("blah")
	c.Assert(err, gc.ErrorMatches, `invalid UUID: "blah"`)
	validUUID := "9f484882-2f18-4fd2-967d-db9663db7bea"
	uuid, err := utils.UUIDFromString(validUUID)
	c.Assert(err, gc.IsNil)
	c.Assert(uuid.String(), gc.Equals, validUUID)
}
<commit_msg>added a couple of tests to IsValidUUID check, restructured tests to make it more readable<commit_after>// Copyright 2013 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.

package utils_test

import (
	"github.com/juju/testing"
	jc "github.com/juju/testing/checkers"
	gc "gopkg.in/check.v1"

	"github.com/juju/utils"
)

type uuidSuite struct {
	testing.IsolationSuite
}

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

func (*uuidSuite) TestUUID(c *gc.C) {
	uuid, err := utils.NewUUID()
	c.Assert(err, gc.IsNil)
	uuidCopy := uuid.Copy()
	uuidRaw := uuid.Raw()
	uuidStr := uuid.String()
	c.Assert(uuidRaw, gc.HasLen, 16)
	c.Assert(uuidStr, jc.Satisfies, utils.IsValidUUIDString)
	uuid[0] = 0x00
	uuidCopy[0] = 0xFF
	c.Assert(uuid, gc.Not(gc.DeepEquals), uuidCopy)
	uuidRaw[0] = 0xFF
	c.Assert(uuid, gc.Not(gc.DeepEquals), uuidRaw)
	nextUUID, err := utils.NewUUID()
	c.Assert(err, gc.IsNil)
	c.Assert(uuid, gc.Not(gc.DeepEquals), nextUUID)
}

func (*uuidSuite) TestIsValidUUIDFailsWhenNotValid(c *gc.C) {
	tests := []struct {
		input    string
		expected bool
	}{
		{
			utils.UUID{}.String(),
			false,
		},
		{
			"",
			false,
		},
		{
			"blah",
			false,
		},
		{
			"blah-9f484882-2f18-4fd2-967d-db9663db7bea",
			false,
		},
		{
			"9f484882-2f18-4fd2-967d-db9663db7bea-blah",
			false,
		},
		{
			"9f484882-2f18-4fd2-967d-db9663db7bea",
			true,
		},
	}
	for i, t := range tests {
		c.Logf("Running test %d", i)
		c.Check(utils.IsValidUUIDString(t.input), gc.Equals, t.expected)
	}
}

func (*uuidSuite) TestUUIDFromString(c *gc.C) {
	_, err := utils.UUIDFromString("blah")
	c.Assert(err, gc.ErrorMatches, `invalid UUID: "blah"`)
	validUUID := "9f484882-2f18-4fd2-967d-db9663db7bea"
	uuid, err := utils.UUIDFromString(validUUID)
	c.Assert(err, gc.IsNil)
	c.Assert(uuid.String(), gc.Equals, validUUID)
}
<|endoftext|>


<commit_before>package client

import "encoding/json"

type SimpleError struct {
	Message string `json:"error"`
}

func (se SimpleError) Error() string {
	return se.Message
}

type ValidationErrors struct {
	Errors map[string][]string `json:"errors"`
}

func (ve ValidationErrors) Error() string {
	b, _ := json.Marshal(ve)
	return string(b)
}
<commit_msg>some more docs<commit_after>package client

import "encoding/json"

// SimpleError wraps simple error messages that come from
// the Enterprise API, such as:
// {"error":"json: cannot unmarshal number into Go value of type string"}
type SimpleError struct {
	Message string `json:"error"`
}

func (se SimpleError) Error() string {
	return se.Message
}

// ValidationErrors wraps more complex validation errors
// that the Enterprise API generates. These most usually come
// as the result of a 422 error.
type ValidationErrors struct {
	Errors map[string][]string `json:"errors"`
}

func (ve ValidationErrors) Error() string {
	b, _ := json.Marshal(ve)
	return string(b)
}
<|endoftext|>


<commit_before>package trie

import (
	"github.com/ikawaha/tokenizer/trie/da"

	"bufio"
	"errors"
	"os"
	"sort"
)

func NewDoubleArrayTrie(a_src interface{}) (Trie, error) {
	switch a_src.(type) {
	case []string:
		return newDoubleArrayTrieKeywords(a_src.([]string)), nil
	case *os.File:
		return newDoubleArrayTrieFile(a_src.(*os.File))
	default:
		return da.NewDoubleArray(), errors.New("cannot open unknown type src, '[]string' or '*os.File' can be specified.")
	}
}

func newDoubleArrayTrieKeywords(a_keywords []string) Trie {
	sort.Strings(a_keywords)
	da := da.NewDoubleArray()
	da.Build(a_keywords)
	return da
}

func newDoubleArrayTrieFile(a_file *os.File) (Trie, error) {
	da := da.NewDoubleArray()
	scanner := bufio.NewScanner(a_file)
	keywords := make([]string, 0, 51200)
	for scanner.Scan() {
		keywords = append(keywords, scanner.Text())
	}
	da.Build(keywords)
	return da, scanner.Err()
}
<commit_msg>fix a bug of the package path.<commit_after>package trie

import (
	"github.com/ikawaha/kagome/trie/da"

	"bufio"
	"errors"
	"os"
	"sort"
)

func NewDoubleArrayTrie(a_src interface{}) (Trie, error) {
	switch a_src.(type) {
	case []string:
		return newDoubleArrayTrieKeywords(a_src.([]string)), nil
	case *os.File:
		return newDoubleArrayTrieFile(a_src.(*os.File))
	default:
		return da.NewDoubleArray(), errors.New("cannot open unknown type src, '[]string' or '*os.File' can be specified.")
	}
}

func newDoubleArrayTrieKeywords(a_keywords []string) Trie {
	sort.Strings(a_keywords)
	da := da.NewDoubleArray()
	da.Build(a_keywords)
	return da
}

func newDoubleArrayTrieFile(a_file *os.File) (Trie, error) {
	da := da.NewDoubleArray()
	scanner := bufio.NewScanner(a_file)
	keywords := make([]string, 0, 51200)
	for scanner.Scan() {
		keywords = append(keywords, scanner.Text())
	}
	da.Build(keywords)
	return da, scanner.Err()
}
<|endoftext|>


<commit_before>package tunnel

import (
	"fmt"
	"net/http"
	"reflect"
	"sync"
	"time"

	"github.com/gorilla/websocket"
	"github.com/looplab/fsm"
	"github.com/ondevice/ondevice/api"
	"github.com/ondevice/ondevice/config"
	"github.com/ondevice/ondevice/logg"
	"github.com/ondevice/ondevice/util"
)

// Connection -- WebSocket connection
//
// internal state machine:
// - initial (OpenWebsocket() sets the state to 'connecting' pretty soon after creating the state machine, so this state isn't all too relevant)
// - connecting: event: "connect", from: initial
// - open: event: "connected", from: connecting
// - error: event: "error", from: initial, connecting, open
// - closed: event: "close", from: initial, connecting, open, error, timeout
type Connection struct {
	ws    *websocket.Conn
	state *fsm.FSM

	CloseListeners    []func()
	ErrorListeners    []func(err util.APIError)
	MessageListerners []func(int, []byte)

	writeLock sync.Mutex
	done      chan struct{}
}

// OpenWebsocket -- Open a websocket connection
func OpenWebsocket(c *Connection, endpoint string, params map[string]string, onMessage func(int, []byte), auths ...api.Authentication) util.APIError {
	if c.state != nil {
		panic("OpenWebsocket() called twice on a single Connection!")
	}
	c.state = fsm.NewFSM("initial", fsm.Events{
		{Name: "connect", Src: []string{"initial"}, Dst: "connecting"},
		{Name: "connected", Src: []string{"connecting"}, Dst: "open"},
		{Name: "error", Src: []string{"initial", "connecting", "open"}, Dst: "error"},
		{Name: "close", Src: []string{"initial", "connecting", "open", "error"}, Dst: "closed"},
	}, fsm.Callbacks{
		"after_error":  c._onError,
		"enter_closed": c._onClose,
		"enter_state":  c._onStatechange,
	})

	hdr := http.Header{}

	var auth api.Authentication
	if len(auths) == 0 {
		var err error
		if auth, err = api.CreateClientAuth(); err != nil {
			return util.NewAPIError(util.OtherError, err.Error())
		}
	} else {
		auth = auths[0]
	}

	hdr.Add("Authorization", auth.GetAuthHeader())
	hdr.Add("User-agent", fmt.Sprintf("ondevice v%s", config.GetVersion()))

	url := auth.GetURL(endpoint+"/websocket", params, "wss")
	logg.Debugf("Opening websocket connection to '%s' (auth: '%s')", url, auth.GetAuthHeader())

	c.state.Event("connect")
	websocket.DefaultDialer.HandshakeTimeout = 60 * time.Second
	ws, resp, err := websocket.DefaultDialer.Dial(url, hdr)
	if err != nil {
		if resp != nil {
			if resp.StatusCode == 401 {
				return util.NewAPIError(resp.StatusCode, "API server authentication failed")
			}
			return util.NewAPIError(resp.StatusCode, "Error opening websocket: ", err)
		}
		return util.NewAPIError(util.OtherError, "Error opening websocket: ", err)
	}

	c.ws = ws
	c.MessageListerners = append(c.MessageListerners, onMessage)
	c.done = make(chan struct{})

	go c.receive()

	return nil
}

// Close -- Close the underlying WebSocket connection
func (c *Connection) Close() {
	if err := c.state.Event("close"); err != nil {
		// TODO do error handling (and ignore 'already in closed state' error)
	}
}

// IsClosed -- Returns true for closed connections (either being closed normally or due to an error/timeout)
func (c *Connection) IsClosed() bool {
	return c.state != nil && c.state.Is("closed")
}

func (c *Connection) receive() {
	defer c.Close()

	for {
		msgType, msg, err := c.ws.ReadMessage()
		if err != nil {
			if e, ok := err.(*websocket.CloseError); ok {
				if e.Code == 1000 {
					// normal close
				} else {
					logg.Error("Websocket closed abnormally: ", err)
				}
			} else {
				if !c.IsClosed() {
					logg.Errorf("read error (type: %s): %s", reflect.TypeOf(err), err)
					c._error(util.NewAPIError(util.OtherError, err.Error()))
				} else {
					logg.Debug("Connetion.receive() interrupted by error: ", reflect.TypeOf(err), err)
				}
			}
			return
		}
		for _, cb := range c.MessageListerners {
			cb(msgType, msg)
		}
	}
}

// SendBinary -- Send binary WebSocket message
func (c *Connection) SendBinary(data []byte) {
	c.writeLock.Lock()
	c.ws.WriteMessage(websocket.BinaryMessage, data)
	c.writeLock.Unlock()
}

// SendJSON -- Send a JSON text message to the WebSocket
func (c *Connection) SendJSON(value interface{}) {
	c.writeLock.Lock()
	c.ws.WriteJSON(value)
	c.writeLock.Unlock()
}

// SendText -- send a raw text websocket messge (use SendJson instead where possible)
func (c *Connection) SendText(msg string) {
	c.writeLock.Lock()
	c.ws.WriteMessage(websocket.TextMessage, []byte(msg))
	c.writeLock.Unlock()
}

// Wait -- Wait for the connection to close
func (c *Connection) Wait() {
	<-c.done
}

// _error -- Puts the connection into the 'error' state and closes it
func (c *Connection) _error(err util.APIError) {
	c.state.Event("error", err)
}

func (c *Connection) _onClose(ev *fsm.Event) {
	if c.ws != nil { // could be nil if a goroutine called us before we are connected
		c.ws.Close()
	}

	close(c.done)
	for _, cb := range c.CloseListeners {
		cb()
	}
}

func (c *Connection) _onError(ev *fsm.Event) {
	err, ok := ev.Args[0].(util.APIError)
	if !ok {
		panic("Connection._onError() expects an APIError parameter!")
	}
	for _, cb := range c.ErrorListeners {
		cb(err)
	}

	c.Close()
}

func (c *Connection) _onStatechange(ev *fsm.Event) {
	logg.Debugf("Connection state changed: ", ev.Src, " -> ", ev.Dst)
}
<commit_msg>fixed capitalization<commit_after>package tunnel

import (
	"fmt"
	"net/http"
	"reflect"
	"sync"
	"time"

	"github.com/gorilla/websocket"
	"github.com/looplab/fsm"
	"github.com/ondevice/ondevice/api"
	"github.com/ondevice/ondevice/config"
	"github.com/ondevice/ondevice/logg"
	"github.com/ondevice/ondevice/util"
)

// Connection -- WebSocket connection
//
// internal state machine:
// - initial (OpenWebsocket() sets the state to 'connecting' pretty soon after creating the state machine, so this state isn't all too relevant)
// - connecting: event: "connect", from: initial
// - open: event: "connected", from: connecting
// - error: event: "error", from: initial, connecting, open
// - closed: event: "close", from: initial, connecting, open, error, timeout
type Connection struct {
	ws    *websocket.Conn
	state *fsm.FSM

	CloseListeners    []func()
	ErrorListeners    []func(err util.APIError)
	MessageListerners []func(int, []byte)

	writeLock sync.Mutex
	done      chan struct{}
}

// OpenWebsocket -- Open a websocket connection
func OpenWebsocket(c *Connection, endpoint string, params map[string]string, onMessage func(int, []byte), auths ...api.Authentication) util.APIError {
	if c.state != nil {
		panic("OpenWebsocket() called twice on a single Connection!")
	}
	c.state = fsm.NewFSM("initial", fsm.Events{
		{Name: "connect", Src: []string{"initial"}, Dst: "connecting"},
		{Name: "connected", Src: []string{"connecting"}, Dst: "open"},
		{Name: "error", Src: []string{"initial", "connecting", "open"}, Dst: "error"},
		{Name: "close", Src: []string{"initial", "connecting", "open", "error"}, Dst: "closed"},
	}, fsm.Callbacks{
		"after_error":  c._onError,
		"enter_closed": c._onClose,
		"enter_state":  c._onStateChange,
	})

	hdr := http.Header{}

	var auth api.Authentication
	if len(auths) == 0 {
		var err error
		if auth, err = api.CreateClientAuth(); err != nil {
			return util.NewAPIError(util.OtherError, err.Error())
		}
	} else {
		auth = auths[0]
	}

	hdr.Add("Authorization", auth.GetAuthHeader())
	hdr.Add("User-agent", fmt.Sprintf("ondevice v%s", config.GetVersion()))

	url := auth.GetURL(endpoint+"/websocket", params, "wss")
	logg.Debugf("Opening websocket connection to '%s' (auth: '%s')", url, auth.GetAuthHeader())

	c.state.Event("connect")
	websocket.DefaultDialer.HandshakeTimeout = 60 * time.Second
	ws, resp, err := websocket.DefaultDialer.Dial(url, hdr)
	if err != nil {
		if resp != nil {
			if resp.StatusCode == 401 {
				return util.NewAPIError(resp.StatusCode, "API server authentication failed")
			}
			return util.NewAPIError(resp.StatusCode, "Error opening websocket: ", err)
		}
		return util.NewAPIError(util.OtherError, "Error opening websocket: ", err)
	}

	c.ws = ws
	c.MessageListerners = append(c.MessageListerners, onMessage)
	c.done = make(chan struct{})

	go c.receive()

	return nil
}

// Close -- Close the underlying WebSocket connection
func (c *Connection) Close() {
	if err := c.state.Event("close"); err != nil {
		// TODO do error handling (and ignore 'already in closed state' error)
	}
}

// IsClosed -- Returns true for closed connections (either being closed normally or due to an error/timeout)
func (c *Connection) IsClosed() bool {
	return c.state != nil && c.state.Is("closed")
}

func (c *Connection) receive() {
	defer c.Close()

	for {
		msgType, msg, err := c.ws.ReadMessage()
		if err != nil {
			if e, ok := err.(*websocket.CloseError); ok {
				if e.Code == 1000 {
					// normal close
				} else {
					logg.Error("Websocket closed abnormally: ", err)
				}
			} else {
				if !c.IsClosed() {
					logg.Errorf("read error (type: %s): %s", reflect.TypeOf(err), err)
					c._error(util.NewAPIError(util.OtherError, err.Error()))
				} else {
					logg.Debug("Connetion.receive() interrupted by error: ", reflect.TypeOf(err), err)
				}
			}
			return
		}
		for _, cb := range c.MessageListerners {
			cb(msgType, msg)
		}
	}
}

// SendBinary -- Send binary WebSocket message
func (c *Connection) SendBinary(data []byte) {
	c.writeLock.Lock()
	c.ws.WriteMessage(websocket.BinaryMessage, data)
	c.writeLock.Unlock()
}

// SendJSON -- Send a JSON text message to the WebSocket
func (c *Connection) SendJSON(value interface{}) {
	c.writeLock.Lock()
	c.ws.WriteJSON(value)
	c.writeLock.Unlock()
}

// SendText -- send a raw text websocket messge (use SendJson instead where possible)
func (c *Connection) SendText(msg string) {
	c.writeLock.Lock()
	c.ws.WriteMessage(websocket.TextMessage, []byte(msg))
	c.writeLock.Unlock()
}

// Wait -- Wait for the connection to close
func (c *Connection) Wait() {
	<-c.done
}

// _error -- Puts the connection into the 'error' state and closes it
func (c *Connection) _error(err util.APIError) {
	c.state.Event("error", err)
}

func (c *Connection) _onClose(ev *fsm.Event) {
	if c.ws != nil { // could be nil if a goroutine called us before we are connected
		c.ws.Close()
	}

	close(c.done)
	for _, cb := range c.CloseListeners {
		cb()
	}
}

func (c *Connection) _onError(ev *fsm.Event) {
	err, ok := ev.Args[0].(util.APIError)
	if !ok {
		panic("Connection._onError() expects an APIError parameter!")
	}
	for _, cb := range c.ErrorListeners {
		cb(err)
	}

	c.Close()
}

func (c *Connection) _onStateChange(ev *fsm.Event) {
	logg.Debugf("Connection state changed: ", ev.Src, " -> ", ev.Dst)
}
<|endoftext|>


<commit_before>package sudoku

import (
	"math"
)

//1.0 is a no op. 0.0 to 1.0 is increase goodness; 1.0 and above is decraase good
type probabilityTweak float64

type probabilityTwiddler func(*SolveStep, []*SolveStep, []*CompoundSolveStep, *Grid) probabilityTweak

//twiddlers is the list of all of the twiddlers we should apply to change the
//probability distribution of possibilities at each step. They capture biases
//that humans have about which cells to focus on (which is separate from
//Technique.humanLikelihood, since that is about how common a technique in
//general, not in a specific context.)
var twiddlers map[string]probabilityTwiddler

func init() {
	twiddlers = map[string]probabilityTwiddler{
		"Human Likelihood": twiddleHumanLikelihood,
		"Chained Steps":    twiddleChainedSteps,
		"Common Numbers":   twiddleCommonNumbers,
	}
}

//twiddleTechniqueWeight is a fundamental twiddler based on the
//HumanLikeliehood of the current technique. In fact, it's so fundamental that
//it's arguably not even a twiddler at all.
func twiddleHumanLikelihood(currentStep *SolveStep, inProgressCompoundStep []*SolveStep, pastSteps []*CompoundSolveStep, grid *Grid) probabilityTweak {
	if currentStep == nil {
		return 1.0
	}
	return probabilityTweak(currentStep.HumanLikelihood())
}

//twiddleCommonNumbers will twiddle up steps whose TargetNumbers are over-
//represented in the grid (but not DIM). This captures that humans, in
//practice, will often choose to look for cells to fill for a number that is
//represented more in the grid, since they're more likely to be constrained by
//neighorbors with the same number.
func twiddleCommonNumbers(currentStep *SolveStep, inProgressCompoundStep []*SolveStep, pastSteps []*CompoundSolveStep, grid *Grid) probabilityTweak {

	//Skip steps that aren't fill or fill multiple
	if !currentStep.Technique.IsFill() || len(currentStep.TargetNums) > 1 {
		return 1.0
	}

	keyNum := currentStep.TargetNums[0]

	count := 0

	for _, cell := range grid.Cells() {
		if cell.Number() == keyNum {
			count++
		}
	}

	if count == 0 || count == DIM {
		count = 1
	}

	//TODO: figure out if this should be on a curve.
	return probabilityTweak(count)

}

//This function will tweak weights quite a bit to make it more likely that we will pick a subsequent step that
// is 'related' to the cells modified in the last step. For example, if the
// last step had targetCells that shared a row, then a step with
//target cells in that same row will be more likely this step. This captures the fact that humans, in practice,
//will have 'chains' of steps that are all related.
func twiddleChainedSteps(currentStep *SolveStep, inProgressCompoundStep []*SolveStep, pastSteps []*CompoundSolveStep, grid *Grid) probabilityTweak {

	if len(inProgressCompoundStep) == 0 {
		return 1.0
	}

	lastModifiedCells := inProgressCompoundStep[len(inProgressCompoundStep)-1].TargetCells

	if lastModifiedCells == nil {
		return 1.0
	}

	//Tweak every weight by how related they are.
	//Remember: these are INVERTED weights, so tweaking them down is BETTER.

	//TODO: consider attentuating the effect of this; chaining is nice but shouldn't totally change the calculation for hard techniques.
	//It turns out that we probably want to STRENGTHEN the effect.
	//Logically we should be attenuating Dissimilarity here, but for some reason the math.Pow(dissimilairty, 10) doesn't actually
	//appear to work here, which is maddening.

	similarity := currentStep.TargetCells.chainSimilarity(lastModifiedCells)
	//Make sure that similarity is higher than 1 so raising 2 to this power will make it go up.
	similarity *= 10

	return probabilityTweak(math.Pow(10, similarity))

}
<commit_msg>TwiddleChainedSteps will look back to the last CompoundStep if there's nothing in inProgressCompoundStep.<commit_after>package sudoku

import (
	"math"
)

//1.0 is a no op. 0.0 to 1.0 is increase goodness; 1.0 and above is decraase good
type probabilityTweak float64

type probabilityTwiddler func(*SolveStep, []*SolveStep, []*CompoundSolveStep, *Grid) probabilityTweak

//twiddlers is the list of all of the twiddlers we should apply to change the
//probability distribution of possibilities at each step. They capture biases
//that humans have about which cells to focus on (which is separate from
//Technique.humanLikelihood, since that is about how common a technique in
//general, not in a specific context.)
var twiddlers map[string]probabilityTwiddler

func init() {
	twiddlers = map[string]probabilityTwiddler{
		"Human Likelihood": twiddleHumanLikelihood,
		"Chained Steps":    twiddleChainedSteps,
		"Common Numbers":   twiddleCommonNumbers,
	}
}

//twiddleTechniqueWeight is a fundamental twiddler based on the
//HumanLikeliehood of the current technique. In fact, it's so fundamental that
//it's arguably not even a twiddler at all.
func twiddleHumanLikelihood(currentStep *SolveStep, inProgressCompoundStep []*SolveStep, pastSteps []*CompoundSolveStep, grid *Grid) probabilityTweak {
	if currentStep == nil {
		return 1.0
	}
	return probabilityTweak(currentStep.HumanLikelihood())
}

//twiddleCommonNumbers will twiddle up steps whose TargetNumbers are over-
//represented in the grid (but not DIM). This captures that humans, in
//practice, will often choose to look for cells to fill for a number that is
//represented more in the grid, since they're more likely to be constrained by
//neighorbors with the same number.
func twiddleCommonNumbers(currentStep *SolveStep, inProgressCompoundStep []*SolveStep, pastSteps []*CompoundSolveStep, grid *Grid) probabilityTweak {

	//Skip steps that aren't fill or fill multiple
	if !currentStep.Technique.IsFill() || len(currentStep.TargetNums) > 1 {
		return 1.0
	}

	keyNum := currentStep.TargetNums[0]

	count := 0

	for _, cell := range grid.Cells() {
		if cell.Number() == keyNum {
			count++
		}
	}

	if count == 0 || count == DIM {
		count = 1
	}

	//TODO: figure out if this should be on a curve.
	return probabilityTweak(count)

}

//This function will tweak weights quite a bit to make it more likely that we will pick a subsequent step that
// is 'related' to the cells modified in the last step. For example, if the
// last step had targetCells that shared a row, then a step with
//target cells in that same row will be more likely this step. This captures the fact that humans, in practice,
//will have 'chains' of steps that are all related.
func twiddleChainedSteps(currentStep *SolveStep, inProgressCompoundStep []*SolveStep, pastSteps []*CompoundSolveStep, grid *Grid) probabilityTweak {

	var lastModifiedCells CellSlice

	if len(inProgressCompoundStep) > 0 {
		lastModifiedCells = inProgressCompoundStep[len(inProgressCompoundStep)-1].TargetCells
	} else if len(pastSteps) > 0 {
		lastCompoundStep := pastSteps[len(pastSteps)-1]
		if lastCompoundStep.FillStep != nil {
			lastModifiedCells = lastCompoundStep.FillStep.TargetCells
		}
	}

	if lastModifiedCells == nil {
		return 1.0
	}

	//Tweak every weight by how related they are.
	//Remember: these are INVERTED weights, so tweaking them down is BETTER.

	//TODO: consider attentuating the effect of this; chaining is nice but shouldn't totally change the calculation for hard techniques.
	//It turns out that we probably want to STRENGTHEN the effect.
	//Logically we should be attenuating Dissimilarity here, but for some reason the math.Pow(dissimilairty, 10) doesn't actually
	//appear to work here, which is maddening.

	similarity := currentStep.TargetCells.chainSimilarity(lastModifiedCells)
	//Make sure that similarity is higher than 1 so raising 2 to this power will make it go up.
	similarity *= 10

	return probabilityTweak(math.Pow(10, similarity))

}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"os"
	"strings"

	"bazil.org/fuse"
	"bazil.org/fuse/fs"
	"github.com/pellaeon/goas/v3/logger"
	"github.com/pellaeon/skicka/gdrive"
	"golang.org/x/net/context"
)

var _ fs.Node = (*Dir)(nil)

type Dir struct {
	sk_file *gdrive.File
	fs      *FS
}

func (n Dir) Attr(ctx context.Context, attr *fuse.Attr) error {
	logger.Debugf("%s .Attr()", n.sk_file.Path)
	var sum uint64
	for _, c := range n.sk_file.Id {
		sum += uint64(c)
	}
	logger.Debugf("%s .Attr() Id= %s", n.sk_file.Path, n.sk_file.Id)
	attr.Inode = sum
	attr.Size = 4096 // XXX
	attr.Blocks = 0
	attr.Atime = n.sk_file.ModTime
	attr.Mtime = n.sk_file.ModTime
	attr.Ctime = n.sk_file.ModTime
	attr.Crtime = n.sk_file.ModTime
	attr.Mode = os.ModeDir | 0755
	attr.Nlink = 0

	return nil
}

type DirHandle struct {
	sk_file *gdrive.File
}

var _ fs.HandleReadDirAller = (*DirHandle)(nil)

func (dh *DirHandle) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
	logger.Debugf("ReadDirAll %s", dh.sk_file.Path)
	files_in_folder, err := gd.GetFilesInFolder(dh.sk_file.Path)
	if err != nil {
		log.Fatalf("ReadDirAll failed: %v", err)
	}
	var res []fuse.Dirent
	for _, file := range files_in_folder {
		var de fuse.Dirent
		de.Name = file.Path[strings.LastIndex(file.Path, "/")+1:]
		logger.Debugf("ReadDirAll %s - %s", dh.sk_file.Path, de.Name)
		if file.IsFolder() {
			de.Type = fuse.DT_Dir
		} else {
			de.Type = fuse.DT_File
		}
		sum := uint64(0)
		for _, c := range file.Id {
			sum += uint64(c)
		}
		de.Inode = sum
		res = append(res, de)
	}
	return res, err
}

var _ fs.NodeRequestLookuper = (*Dir)(nil)

func (n Dir) Lookup(ctx context.Context, req *fuse.LookupRequest, resp *fuse.LookupResponse) (fs.Node, error) {
	path := req.Name
	logger.Debugf("req.Name= " + req.Name)
	gd_file, err := n.fs.gd.GetFile(path)
	if err != nil {
		log.Panicf("Lookup GetFile failed: %v", err)
		return nil, fuse.ENOENT
	}
	if gd_file.IsFolder() {
		return Dir{
			sk_file: gd_file,
			fs:      n.fs,
		}, nil
	} else {
		return File{
			sk_file: gd_file,
			fs:      n.fs,
		}, nil
	}
}

var _ fs.NodeOpener = (*Dir)(nil)

func (n Dir) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) (fs.Handle, error) {
	logger.Debugf("Dir.Open()")
	resp.Flags |= fuse.OpenNonSeekable
	return &DirHandle{
		sk_file: n.sk_file,
	}, nil
}

var _ fs.NodeAccesser = (*Dir)(nil)

func (d Dir) Access(ctx context.Context, req *fuse.AccessRequest) error {
	logger.Debugf(d.sk_file.Path + " Access()")
	return nil
}
<commit_msg>Fix# construct full path for gd.GetFile() in Lookup()<commit_after>package main

import (
	"log"
	"os"
	"strings"

	"bazil.org/fuse"
	"bazil.org/fuse/fs"
	"github.com/pellaeon/goas/v3/logger"
	"github.com/pellaeon/skicka/gdrive"
	"golang.org/x/net/context"
)

var _ fs.Node = (*Dir)(nil)

type Dir struct {
	sk_file *gdrive.File
	fs      *FS
}

func (n Dir) Attr(ctx context.Context, attr *fuse.Attr) error {
	logger.Debugf("%s .Attr()", n.sk_file.Path)
	var sum uint64
	for _, c := range n.sk_file.Id {
		sum += uint64(c)
	}
	logger.Debugf("%s .Attr() Id= %s", n.sk_file.Path, n.sk_file.Id)
	attr.Inode = sum
	attr.Size = 4096 // XXX
	attr.Blocks = 0
	attr.Atime = n.sk_file.ModTime
	attr.Mtime = n.sk_file.ModTime
	attr.Ctime = n.sk_file.ModTime
	attr.Crtime = n.sk_file.ModTime
	attr.Mode = os.ModeDir | 0755
	attr.Nlink = 0

	return nil
}

type DirHandle struct {
	sk_file *gdrive.File
}

var _ fs.HandleReadDirAller = (*DirHandle)(nil)

func (dh *DirHandle) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
	logger.Debugf("ReadDirAll %s", dh.sk_file.Path)
	files_in_folder, err := gd.GetFilesInFolder(dh.sk_file.Path)
	if err != nil {
		log.Fatalf("ReadDirAll failed: %v", err)
	}
	var res []fuse.Dirent
	for _, file := range files_in_folder {
		var de fuse.Dirent
		de.Name = file.Path[strings.LastIndex(file.Path, "/")+1:]
		logger.Debugf("ReadDirAll %s - %s", dh.sk_file.Path, de.Name)
		if file.IsFolder() {
			de.Type = fuse.DT_Dir
		} else {
			de.Type = fuse.DT_File
		}
		sum := uint64(0)
		for _, c := range file.Id {
			sum += uint64(c)
		}
		de.Inode = sum
		res = append(res, de)
	}
	return res, err
}

var _ fs.NodeRequestLookuper = (*Dir)(nil)

func (n Dir) Lookup(ctx context.Context, req *fuse.LookupRequest, resp *fuse.LookupResponse) (fs.Node, error) {
	path := n.sk_file.Path + "/" + req.Name
	logger.Debugf("req.Name= " + req.Name)
	gd_file, err := n.fs.gd.GetFile(path)
	if err != nil {
		log.Panicf("Lookup GetFile failed: %v", err)
		return nil, fuse.ENOENT
	}
	if gd_file.IsFolder() {
		return Dir{
			sk_file: gd_file,
			fs:      n.fs,
		}, nil
	} else {
		return File{
			sk_file: gd_file,
			fs:      n.fs,
		}, nil
	}
}

var _ fs.NodeOpener = (*Dir)(nil)

func (n Dir) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) (fs.Handle, error) {
	logger.Debugf("Dir.Open()")
	resp.Flags |= fuse.OpenNonSeekable
	return &DirHandle{
		sk_file: n.sk_file,
	}, nil
}

var _ fs.NodeAccesser = (*Dir)(nil)

func (d Dir) Access(ctx context.Context, req *fuse.AccessRequest) error {
	logger.Debugf(d.sk_file.Path + " Access()")
	return nil
}
<|endoftext|>