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

// Package bce define a set of core data structure and functions for baidubce.
package bce

import (
	"fmt"
	"net/http"
	"net/url"
	"runtime"
	"sort"
	"strconv"
	"strings"
	"time"

	"github.com/guoyao/baidubce-sdk-go/util"
)

const (
	Version = "0.1.0"
	// ExpirationPeriodInSeconds 1800s is the default expiration period.
	ExpirationPeriodInSeconds = 1800
)

var DefaultUserAgent = strings.Join([]string{
	"baidubce-sdk-go",
	Version,
	runtime.GOOS,
	runtime.Version(),
}, "/")

var Region = map[string]string{
	"bj": "bj",
	"gz": "gz",
}

// Credentials struct for baidubce.
type Credentials struct {
	AccessKeyID     string
	SecretAccessKey string
}

// NewCredentials returns an instance of type `Credentials`.
func NewCredentials(AccessKeyID, secretAccessKey string) *Credentials {
	return &Credentials{AccessKeyID, secretAccessKey}
}

// Config contains options for baidubce api.
type Config struct {
	*Credentials
	Region     string
	Endpoint   string
	APIVersion string
	Protocol   string
	UserAgent  string
	ProxyHost  string
	ProxyPort  int
	//ConnectionTimeoutInMillis time.Duration // default value: 10 * time.Second in http.DefaultTransport
	MaxConnections int           // default value: 2 in http.DefaultMaxIdleConnsPerHost
	Timeout        time.Duration // default value: 0 in http.Client
}

func NewConfig(credentials *Credentials) *Config {
	return &Config{
		Credentials: credentials,
		Region:      Region["bj"],
	}
}

func (config *Config) GetRegion() string {
	region := config.Region

	if region == "" {
		region = Region["bj"]
	}

	return region
}

func (config *Config) GetUserAgent() string {
	userAgent := config.UserAgent

	if userAgent == "" {
		userAgent = DefaultUserAgent
	}

	return userAgent
}

// SignOption contains options for signature of baidubce api.
type SignOption struct {
	Timestamp                 string
	ExpirationPeriodInSeconds int
	Headers                   map[string]string
	HeadersToSign             []string
	headersToSignSpecified    bool
}

// NewSignOption is the instance factory for `SignOption`.
func NewSignOption(timestamp string, expirationPeriodInSeconds int,
	headers map[string]string, headersToSign []string) *SignOption {

	return &SignOption{timestamp, expirationPeriodInSeconds,
		headers, headersToSign, len(headersToSign) > 0}
}

func CheckSignOption(option *SignOption) *SignOption {
	if option == nil {
		return &SignOption{}
	}

	return option
}

func (option *SignOption) AddHeadersToSign(headers ...string) {
	if option.HeadersToSign == nil {
		option.HeadersToSign = []string{}
		option.HeadersToSign = append(option.HeadersToSign, headers...)
	} else {
		for _, header := range headers {
			if !util.Contains(option.HeadersToSign, header, true) {
				option.HeadersToSign = append(option.HeadersToSign, header)
			}
		}
	}
}

func (option *SignOption) AddHeader(key, value string) {
	if option.Headers == nil {
		option.Headers = make(map[string]string)
		option.Headers[key] = value
	}

	if !util.MapContains(option.Headers, generateHeaderValidCompareFunc(key)) {
		option.Headers[key] = value
	}
}

func (option *SignOption) AddHeaders(headers map[string]string) {
	if headers == nil {
		return
	}

	if option.Headers == nil {
		option.Headers = make(map[string]string)
	}

	for key, value := range headers {
		option.AddHeader(key, value)
	}
}

func (option *SignOption) init() {
	if option.Timestamp == "" {
		option.Timestamp = util.TimeToUTCString(time.Now())
	}

	if option.ExpirationPeriodInSeconds <= 0 {
		option.ExpirationPeriodInSeconds = ExpirationPeriodInSeconds
	}

	if option.Headers == nil {
		option.Headers = make(map[string]string, 3)
	} else {
		util.MapKeyToLower(option.Headers)
	}

	option.headersToSignSpecified = len(option.HeadersToSign) > 0
	util.SliceToLower(option.HeadersToSign)

	if !util.Contains(option.HeadersToSign, "host", true) {
		option.HeadersToSign = append(option.HeadersToSign, "host")
	}

	if !option.headersToSignSpecified {
		option.HeadersToSign = append(option.HeadersToSign, "x-bce-date")
		option.Headers["x-bce-date"] = option.Timestamp
	} else if util.Contains(option.HeadersToSign, "date", true) {
		if !util.MapContains(option.Headers, generateHeaderValidCompareFunc("date")) {
			option.Headers["date"] = time.Now().Format(time.RFC1123)
		} else {
			option.Headers["date"] = util.TimeStringToRFC1123(util.GetMapValue(option.Headers, "date", true))
		}
	} else if util.Contains(option.HeadersToSign, "x-bce-date", true) {
		if !util.MapContains(option.Headers, generateHeaderValidCompareFunc("x-bce-date")) {
			option.Headers["x-bce-date"] = option.Timestamp
		}
	}
}

func (option *SignOption) signedHeadersToString() string {
	var result string
	length := len(option.HeadersToSign)

	if option.headersToSignSpecified && length > 0 {
		headers := make([]string, 0, length)
		headers = append(headers, option.HeadersToSign...)
		sort.Strings(headers)
		result = strings.Join(headers, ";")
	}

	return result
}

// GenerateAuthorization returns authorization code of baidubce api.
func GenerateAuthorization(credentials Credentials, req Request, option *SignOption) string {
	if option == nil {
		option = &SignOption{}
	}
	option.init()

	authorization := "bce-auth-v1/" + credentials.AccessKeyID
	authorization += "/" + option.Timestamp
	authorization += "/" + strconv.Itoa(option.ExpirationPeriodInSeconds)
	signature := sign(credentials, req, option)
	authorization += "/" + option.signedHeadersToString() + "/" + signature

	req.addHeader("Authorization", authorization)

	return authorization
}

// Client is the base client struct for all products of baidubce.
type Client struct {
	*Config
}

func NewClient(config *Config) *Client {
	return &Client{config}
}

func (c *Client) GetURL(host, uriPath string, params map[string]string) string {
	if strings.Index(uriPath, "/") == 0 {
		uriPath = uriPath[1:]
	}

	if c.APIVersion != "" {
		uriPath = fmt.Sprintf("%s/%s", c.APIVersion, uriPath)
	}

	return util.GetURL(c.Protocol, host, uriPath, params)
}

// SendRequest sends a http request to the endpoint of baidubce api.
func (c *Client) SendRequest(req *Request, option *SignOption) (*Response, *Error) {

	if option == nil {
		option = &SignOption{}
	}

	option.AddHeader("User-Agent", c.GetUserAgent())
	GenerateAuthorization(*c.Credentials, *req, option)

	transport := new(http.Transport)

	if defaultTransport, ok := http.DefaultTransport.(*http.Transport); ok {
		transport.Proxy = defaultTransport.Proxy
		transport.Dial = defaultTransport.Dial
		transport.TLSHandshakeTimeout = defaultTransport.TLSHandshakeTimeout
		transport.ExpectContinueTimeout = defaultTransport.ExpectContinueTimeout
	}

	if c.ProxyHost != "" {
		host := c.ProxyHost

		if c.ProxyPort > 0 {
			host += ":" + strconv.Itoa(c.ProxyPort)
		}

		proxyUrl, err := url.Parse(util.HostToURL(host, "http"))

		if err != nil {
			return nil, NewError(err)
		}

		transport.Proxy = http.ProxyURL(proxyUrl)
	}

	/*
		if c.ConnectionTimeout > 0 {
			transport.TLSHandshakeTimeout = c.ConnectionTimeout
		}
	*/

	if c.MaxConnections > 0 {
		transport.MaxIdleConnsPerHost = c.MaxConnections
	}

	httpClient := http.Client{
		Transport: transport,
		Timeout:   c.Timeout,
	}

	res, err := httpClient.Do(req.raw())

	if err != nil {
		return nil, NewError(err)
	}

	bceResponse := NewResponse(res)

	if res.StatusCode >= http.StatusBadRequest {
		bodyContent, err := bceResponse.GetBodyContent()

		var bceError *Error

		if err != nil {
			bceError = NewError(err)
		}

		if bceError == nil {
			bceError = NewErrorFromJSON(bodyContent)
		}

		bceError.StatusCode = res.StatusCode

		return bceResponse, bceError
	}

	return bceResponse, nil
}

func generateHeaderValidCompareFunc(headerKey string) func(string, string) bool {
	return func(key, value string) bool {
		return strings.ToLower(key) == strings.ToLower(headerKey) && value != ""
	}
}

// sign returns signed signature.
func sign(credentials Credentials, req Request, option *SignOption) string {
	signingKey := getSigningKey(credentials, option)
	req.prepareHeaders(option)
	canonicalRequest := req.canonical(option)
	signature := util.HmacSha256Hex(signingKey, canonicalRequest)

	return signature
}

func getSigningKey(credentials Credentials, option *SignOption) string {
	var authStringPrefix = fmt.Sprintf("bce-auth-v1/%s", credentials.AccessKeyID)
	authStringPrefix += "/" + option.Timestamp
	authStringPrefix += "/" + strconv.Itoa(option.ExpirationPeriodInSeconds)

	return util.HmacSha256Hex(credentials.SecretAccessKey, authStringPrefix)
}
<commit_msg>use single http client, not per request one http client<commit_after>/**
 * Copyright (c) 2015 Guoyao Wu, All Rights Reserved
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 *
 * @file core.go
 * @author guoyao
 */

// Package bce define a set of core data structure and functions for baidubce.
package bce

import (
	"fmt"
	"net/http"
	"net/url"
	"runtime"
	"sort"
	"strconv"
	"strings"
	"time"

	"github.com/guoyao/baidubce-sdk-go/util"
)

const (
	Version = "0.1.0"
	// ExpirationPeriodInSeconds 1800s is the default expiration period.
	ExpirationPeriodInSeconds = 1800
)

var DefaultUserAgent = strings.Join([]string{
	"baidubce-sdk-go",
	Version,
	runtime.GOOS,
	runtime.Version(),
}, "/")

var Region = map[string]string{
	"bj": "bj",
	"gz": "gz",
}

// Credentials struct for baidubce.
type Credentials struct {
	AccessKeyID     string
	SecretAccessKey string
}

// NewCredentials returns an instance of type `Credentials`.
func NewCredentials(AccessKeyID, secretAccessKey string) *Credentials {
	return &Credentials{AccessKeyID, secretAccessKey}
}

// Config contains options for baidubce api.
type Config struct {
	*Credentials
	Region     string
	Endpoint   string
	APIVersion string
	Protocol   string
	UserAgent  string
	ProxyHost  string
	ProxyPort  int
	//ConnectionTimeoutInMillis time.Duration // default value: 10 * time.Second in http.DefaultTransport
	MaxConnections int           // default value: 2 in http.DefaultMaxIdleConnsPerHost
	Timeout        time.Duration // default value: 0 in http.Client
}

func NewConfig(credentials *Credentials) *Config {
	return &Config{
		Credentials: credentials,
		Region:      Region["bj"],
	}
}

func (config *Config) GetRegion() string {
	region := config.Region

	if region == "" {
		region = Region["bj"]
	}

	return region
}

func (config *Config) GetUserAgent() string {
	userAgent := config.UserAgent

	if userAgent == "" {
		userAgent = DefaultUserAgent
	}

	return userAgent
}

// SignOption contains options for signature of baidubce api.
type SignOption struct {
	Timestamp                 string
	ExpirationPeriodInSeconds int
	Headers                   map[string]string
	HeadersToSign             []string
	headersToSignSpecified    bool
}

// NewSignOption is the instance factory for `SignOption`.
func NewSignOption(timestamp string, expirationPeriodInSeconds int,
	headers map[string]string, headersToSign []string) *SignOption {

	return &SignOption{timestamp, expirationPeriodInSeconds,
		headers, headersToSign, len(headersToSign) > 0}
}

func CheckSignOption(option *SignOption) *SignOption {
	if option == nil {
		return &SignOption{}
	}

	return option
}

func (option *SignOption) AddHeadersToSign(headers ...string) {
	if option.HeadersToSign == nil {
		option.HeadersToSign = []string{}
		option.HeadersToSign = append(option.HeadersToSign, headers...)
	} else {
		for _, header := range headers {
			if !util.Contains(option.HeadersToSign, header, true) {
				option.HeadersToSign = append(option.HeadersToSign, header)
			}
		}
	}
}

func (option *SignOption) AddHeader(key, value string) {
	if option.Headers == nil {
		option.Headers = make(map[string]string)
		option.Headers[key] = value
	}

	if !util.MapContains(option.Headers, generateHeaderValidCompareFunc(key)) {
		option.Headers[key] = value
	}
}

func (option *SignOption) AddHeaders(headers map[string]string) {
	if headers == nil {
		return
	}

	if option.Headers == nil {
		option.Headers = make(map[string]string)
	}

	for key, value := range headers {
		option.AddHeader(key, value)
	}
}

func (option *SignOption) init() {
	if option.Timestamp == "" {
		option.Timestamp = util.TimeToUTCString(time.Now())
	}

	if option.ExpirationPeriodInSeconds <= 0 {
		option.ExpirationPeriodInSeconds = ExpirationPeriodInSeconds
	}

	if option.Headers == nil {
		option.Headers = make(map[string]string, 3)
	} else {
		util.MapKeyToLower(option.Headers)
	}

	option.headersToSignSpecified = len(option.HeadersToSign) > 0
	util.SliceToLower(option.HeadersToSign)

	if !util.Contains(option.HeadersToSign, "host", true) {
		option.HeadersToSign = append(option.HeadersToSign, "host")
	}

	if !option.headersToSignSpecified {
		option.HeadersToSign = append(option.HeadersToSign, "x-bce-date")
		option.Headers["x-bce-date"] = option.Timestamp
	} else if util.Contains(option.HeadersToSign, "date", true) {
		if !util.MapContains(option.Headers, generateHeaderValidCompareFunc("date")) {
			option.Headers["date"] = time.Now().Format(time.RFC1123)
		} else {
			option.Headers["date"] = util.TimeStringToRFC1123(util.GetMapValue(option.Headers, "date", true))
		}
	} else if util.Contains(option.HeadersToSign, "x-bce-date", true) {
		if !util.MapContains(option.Headers, generateHeaderValidCompareFunc("x-bce-date")) {
			option.Headers["x-bce-date"] = option.Timestamp
		}
	}
}

func (option *SignOption) signedHeadersToString() string {
	var result string
	length := len(option.HeadersToSign)

	if option.headersToSignSpecified && length > 0 {
		headers := make([]string, 0, length)
		headers = append(headers, option.HeadersToSign...)
		sort.Strings(headers)
		result = strings.Join(headers, ";")
	}

	return result
}

// GenerateAuthorization returns authorization code of baidubce api.
func GenerateAuthorization(credentials Credentials, req Request, option *SignOption) string {
	if option == nil {
		option = &SignOption{}
	}
	option.init()

	authorization := "bce-auth-v1/" + credentials.AccessKeyID
	authorization += "/" + option.Timestamp
	authorization += "/" + strconv.Itoa(option.ExpirationPeriodInSeconds)
	signature := sign(credentials, req, option)
	authorization += "/" + option.signedHeadersToString() + "/" + signature

	req.addHeader("Authorization", authorization)

	return authorization
}

// Client is the base client struct for all products of baidubce.
type Client struct {
	*Config
	httpClient *http.Client
}

func NewClient(config *Config) *Client {
	return &Client{config, newHttpClient(config)}
}

func newHttpClient(config *Config) *http.Client {
	transport := new(http.Transport)

	if defaultTransport, ok := http.DefaultTransport.(*http.Transport); ok {
		transport.Proxy = defaultTransport.Proxy
		transport.Dial = defaultTransport.Dial
		transport.TLSHandshakeTimeout = defaultTransport.TLSHandshakeTimeout
		transport.ExpectContinueTimeout = defaultTransport.ExpectContinueTimeout
	}

	if config.ProxyHost != "" {
		host := config.ProxyHost

		if config.ProxyPort > 0 {
			host += ":" + strconv.Itoa(config.ProxyPort)
		}

		proxyUrl, err := url.Parse(util.HostToURL(host, "http"))

		if err != nil {
			panic(err)
		}

		transport.Proxy = http.ProxyURL(proxyUrl)
	}

	/*
		if c.ConnectionTimeout > 0 {
			transport.TLSHandshakeTimeout = c.ConnectionTimeout
		}
	*/

	if config.MaxConnections > 0 {
		transport.MaxIdleConnsPerHost = config.MaxConnections
	}

	return &http.Client{
		Transport: transport,
		Timeout:   config.Timeout,
	}
}

func (c *Client) GetURL(host, uriPath string, params map[string]string) string {
	if strings.Index(uriPath, "/") == 0 {
		uriPath = uriPath[1:]
	}

	if c.APIVersion != "" {
		uriPath = fmt.Sprintf("%s/%s", c.APIVersion, uriPath)
	}

	return util.GetURL(c.Protocol, host, uriPath, params)
}

// SendRequest sends a http request to the endpoint of baidubce api.
func (c *Client) SendRequest(req *Request, option *SignOption) (*Response, *Error) {
	if option == nil {
		option = &SignOption{}
	}

	option.AddHeader("User-Agent", c.GetUserAgent())
	GenerateAuthorization(*c.Credentials, *req, option)

	res, err := c.httpClient.Do(req.raw())

	if err != nil {
		return nil, NewError(err)
	}

	bceResponse := NewResponse(res)

	if res.StatusCode >= http.StatusBadRequest {
		bodyContent, err := bceResponse.GetBodyContent()

		var bceError *Error

		if err != nil {
			bceError = NewError(err)
		}

		if bceError == nil {
			bceError = NewErrorFromJSON(bodyContent)
		}

		bceError.StatusCode = res.StatusCode

		return bceResponse, bceError
	}

	return bceResponse, nil
}

func generateHeaderValidCompareFunc(headerKey string) func(string, string) bool {
	return func(key, value string) bool {
		return strings.ToLower(key) == strings.ToLower(headerKey) && value != ""
	}
}

// sign returns signed signature.
func sign(credentials Credentials, req Request, option *SignOption) string {
	signingKey := getSigningKey(credentials, option)
	req.prepareHeaders(option)
	canonicalRequest := req.canonical(option)
	signature := util.HmacSha256Hex(signingKey, canonicalRequest)

	return signature
}

func getSigningKey(credentials Credentials, option *SignOption) string {
	var authStringPrefix = fmt.Sprintf("bce-auth-v1/%s", credentials.AccessKeyID)
	authStringPrefix += "/" + option.Timestamp
	authStringPrefix += "/" + strconv.Itoa(option.ExpirationPeriodInSeconds)

	return util.HmacSha256Hex(credentials.SecretAccessKey, authStringPrefix)
}
<|endoftext|>


<commit_before>// Package Bitcoind is client librarie for bitcoind JSON RPC API

package bitcoind

import (
	"encoding/json"
	"errors"
	"fmt"
	"strconv"
)

const (
	// VERSION represents bicoind package version
	VERSION = 0.1
	// DEFAULT_RPCCLIENT_TIMEOUT represent http timeout for rcp client
	RPCCLIENT_TIMEOUT = 30
)

// A bitpay represents a bitpay client wrapper
type Bitcoind struct {
	client *rpcClient
}

// New return a new bitcoind
func New(host string, port int, user, passwd string, useSSL bool) (*Bitcoind, error) {
	rpcClient, err := newClient(host, port, user, passwd, useSSL)
	if err != nil {
		return nil, err
	}
	return &Bitcoind{rpcClient}, nil
}

// BackupWallet Safely copies wallet.dat to destination,
// which can be a directory or a path with filename on the remote server
func (b *Bitcoind) BackupWallet(destination string) error {
	r, err := b.client.call("backupwallet", []string{destination})
	return handleError(err, &r)
}

// DumpPrivKey return private key as string associated to public <address>
func (b *Bitcoind) DumpPrivKey(address string) (privKey string, err error) {
	r, err := b.client.call("dumpprivkey", []string{address})
	if err = handleError(err, &r); err != nil {
		return
	}
	err = json.Unmarshal(r.Result, &privKey)
	return
}

// EncryptWallet encrypts the wallet with <passphrase>.
func (b *Bitcoind) EncryptWallet(passphrase string) error {
	r, err := b.client.call("encryptwallet", []string{passphrase})
	return handleError(err, &r)
}

// GetAccount returns the account associated with the given address.
func (b *Bitcoind) GetAccount(address string) (account string, err error) {
	r, err := b.client.call("getaccount", []string{address})
	if err = handleError(err, &r); err != nil {
		return
	}
	err = json.Unmarshal(r.Result, &account)
	return
}

// GetAccountAddress Returns the current bitcoin address for receiving
// payments to this account.
// If account does not exist, it will be created along with an
// associated new address that will be returned.
func (b *Bitcoind) GetAccountAddress(account string) (address string, err error) {
	r, err := b.client.call("getaccountaddress", []string{account})
	if err = handleError(err, &r); err != nil {
		return
	}
	err = json.Unmarshal(r.Result, &address)
	return
}

// GetBalance return the balance of the server or of a specific account
//If [account] is "", returns the server's total available balance.
//If [account] is specified, returns the balance in the account
func (b *Bitcoind) GetBalance(account string, minconf uint64) (balance float64, err error) {
	r, err := b.client.call("getbalance", []interface{}{account, minconf})
	if err = handleError(err, &r); err != nil {
		return
	}
	balance, err = strconv.ParseFloat(string(r.Result), 64)
	return
}

// GetBestBlockhash returns the hash of the best (tip) block in the longest block chain.
func (b *Bitcoind) GetBestBlockhash() (bestBlockHash string, err error) {
	r, err := b.client.call("getbestblockhash", nil)
	if err = handleError(err, &r); err != nil {
		return
	}
	err = json.Unmarshal(r.Result, &bestBlockHash)
	return
}

// GetBlock returns information about the block with the given hash.
func (b *Bitcoind) GetBlock(blockHash string) (block block, err error) {
	r, err := b.client.call("getblock", []string{blockHash})
	if err = handleError(err, &r); err != nil {
		return
	}
	err = json.Unmarshal(r.Result, &block)
	return
}

// GetBlockCount returns the number of blocks in the longest block chain.
func (b *Bitcoind) GetBlockCount() (count uint64, err error) {
	r, err := b.client.call("getblockcount", nil)
	if err = handleError(err, &r); err != nil {
		return
	}
	count, err = strconv.ParseUint(string(r.Result), 10, 64)
	return
}

// GetBlockHash returns hash of block in best-block-chain at <index>
func (b *Bitcoind) GetBlockHash(index uint64) (hash string, err error) {
	r, err := b.client.call("getblockhash", []uint64{index})
	if err = handleError(err, &r); err != nil {
		return
	}
	err = json.Unmarshal(r.Result, &hash)
	return
}

// getBlockTemplateParams reperesent parameters for GetBlockTemplate
type getBlockTemplateParams struct {
	Mode         string   `json:"mode,omitempty"`
	Capabilities []string `json:"capabilities,omitempty"`
}

// TODO a finir
// GetBlockTemplate Returns data needed to construct a block to work on.
// See BIP_0022 for more info on params.
func (b *Bitcoind) GetBlockTemplate(capabilities []string, mode string) (template string, err error) {
	params := getBlockTemplateParams{
		Mode:         mode,
		Capabilities: capabilities,
	}
	// TODO []interface{}{mode, capa}
	r, err := b.client.call("getblocktemplate", []getBlockTemplateParams{params})
	if err = handleError(err, &r); err != nil {
		return
	}
	fmt.Println(json.Unmarshal(r.Result, &template))
	return
}

// GetConnectionCount returns the number of connections to other nodes.
func (b *Bitcoind) GetConnectionCount() (count uint64, err error) {
	r, err := b.client.call("getconnectioncount", nil)
	if err = handleError(err, &r); err != nil {
		return
	}
	count, err = strconv.ParseUint(string(r.Result), 10, 64)
	return
}

// GetDifficulty returns the proof-of-work difficulty as a multiple of
// the minimum difficulty.
func (b *Bitcoind) GetDifficulty() (difficulty float64, err error) {
	r, err := b.client.call("getdifficulty", nil)
	if err = handleError(err, &r); err != nil {
		return
	}
	difficulty, err = strconv.ParseFloat(string(r.Result), 64)
	return
}

// GetInfo return result of "getinfo" command (Amazing !)
func (b *Bitcoind) GetInfo() (i info, err error) {
	r, err := b.client.call("getinfo", nil)
	if err = handleError(err, &r); err != nil {
		return
	}
	err = json.Unmarshal(r.Result, &i)
	return
}

// GetNewAddress return a new address for account [account].
func (b *Bitcoind) GetNewAddress(account ...string) (addr string, err error) {
	// 0 or 1 account
	if len(account) > 1 {
		err = errors.New("Bad parameters for GetNewAddress: you can set 0 or 1 account")
		return
	}
	r, err := b.client.call("getnewaddress", account)
	if err = handleError(err, &r); err != nil {
		return
	}
	err = json.Unmarshal(r.Result, &addr)
	return
}

// GetAddressesByAccount return addresses associated with account <account>
func (b *Bitcoind) GetAddressesByAccount(account string) (addresses []string, err error) {
	r, err := b.client.call("getaddressesbyaccount", []string{account})
	if err = handleError(err, &r); err != nil {
		return
	}
	err = json.Unmarshal(r.Result, &addresses)
	return
}

// walletPassphrase stores the wallet decryption key in memory for <timeout> seconds.
func (b *Bitcoind) WalletPassphrase(passPhrase string, timeout uint64) error {
	r, err := b.client.call("walletpassphrase", []interface{}{passPhrase, timeout})
	return handleError(err, &r)
}
<commit_msg>Typo in com<commit_after>// Package Bitcoind is client librarie for bitcoind JSON RPC API

package bitcoind

import (
	"encoding/json"
	"errors"
	"fmt"
	"strconv"
)

const (
	// VERSION represents bicoind package version
	VERSION = 0.1
	// DEFAULT_RPCCLIENT_TIMEOUT represent http timeout for rcp client
	RPCCLIENT_TIMEOUT = 30
)

// A Bitcoind represents a Bitcoind client
type Bitcoind struct {
	client *rpcClient
}

// New return a new bitcoind
func New(host string, port int, user, passwd string, useSSL bool) (*Bitcoind, error) {
	rpcClient, err := newClient(host, port, user, passwd, useSSL)
	if err != nil {
		return nil, err
	}
	return &Bitcoind{rpcClient}, nil
}

// BackupWallet Safely copies wallet.dat to destination,
// which can be a directory or a path with filename on the remote server
func (b *Bitcoind) BackupWallet(destination string) error {
	r, err := b.client.call("backupwallet", []string{destination})
	return handleError(err, &r)
}

// DumpPrivKey return private key as string associated to public <address>
func (b *Bitcoind) DumpPrivKey(address string) (privKey string, err error) {
	r, err := b.client.call("dumpprivkey", []string{address})
	if err = handleError(err, &r); err != nil {
		return
	}
	err = json.Unmarshal(r.Result, &privKey)
	return
}

// EncryptWallet encrypts the wallet with <passphrase>.
func (b *Bitcoind) EncryptWallet(passphrase string) error {
	r, err := b.client.call("encryptwallet", []string{passphrase})
	return handleError(err, &r)
}

// GetAccount returns the account associated with the given address.
func (b *Bitcoind) GetAccount(address string) (account string, err error) {
	r, err := b.client.call("getaccount", []string{address})
	if err = handleError(err, &r); err != nil {
		return
	}
	err = json.Unmarshal(r.Result, &account)
	return
}

// GetAccountAddress Returns the current bitcoin address for receiving
// payments to this account.
// If account does not exist, it will be created along with an
// associated new address that will be returned.
func (b *Bitcoind) GetAccountAddress(account string) (address string, err error) {
	r, err := b.client.call("getaccountaddress", []string{account})
	if err = handleError(err, &r); err != nil {
		return
	}
	err = json.Unmarshal(r.Result, &address)
	return
}

// GetBalance return the balance of the server or of a specific account
//If [account] is "", returns the server's total available balance.
//If [account] is specified, returns the balance in the account
func (b *Bitcoind) GetBalance(account string, minconf uint64) (balance float64, err error) {
	r, err := b.client.call("getbalance", []interface{}{account, minconf})
	if err = handleError(err, &r); err != nil {
		return
	}
	balance, err = strconv.ParseFloat(string(r.Result), 64)
	return
}

// GetBestBlockhash returns the hash of the best (tip) block in the longest block chain.
func (b *Bitcoind) GetBestBlockhash() (bestBlockHash string, err error) {
	r, err := b.client.call("getbestblockhash", nil)
	if err = handleError(err, &r); err != nil {
		return
	}
	err = json.Unmarshal(r.Result, &bestBlockHash)
	return
}

// GetBlock returns information about the block with the given hash.
func (b *Bitcoind) GetBlock(blockHash string) (block block, err error) {
	r, err := b.client.call("getblock", []string{blockHash})
	if err = handleError(err, &r); err != nil {
		return
	}
	err = json.Unmarshal(r.Result, &block)
	return
}

// GetBlockCount returns the number of blocks in the longest block chain.
func (b *Bitcoind) GetBlockCount() (count uint64, err error) {
	r, err := b.client.call("getblockcount", nil)
	if err = handleError(err, &r); err != nil {
		return
	}
	count, err = strconv.ParseUint(string(r.Result), 10, 64)
	return
}

// GetBlockHash returns hash of block in best-block-chain at <index>
func (b *Bitcoind) GetBlockHash(index uint64) (hash string, err error) {
	r, err := b.client.call("getblockhash", []uint64{index})
	if err = handleError(err, &r); err != nil {
		return
	}
	err = json.Unmarshal(r.Result, &hash)
	return
}

// getBlockTemplateParams reperesent parameters for GetBlockTemplate
type getBlockTemplateParams struct {
	Mode         string   `json:"mode,omitempty"`
	Capabilities []string `json:"capabilities,omitempty"`
}

// TODO a finir
// GetBlockTemplate Returns data needed to construct a block to work on.
// See BIP_0022 for more info on params.
func (b *Bitcoind) GetBlockTemplate(capabilities []string, mode string) (template string, err error) {
	params := getBlockTemplateParams{
		Mode:         mode,
		Capabilities: capabilities,
	}
	// TODO []interface{}{mode, capa}
	r, err := b.client.call("getblocktemplate", []getBlockTemplateParams{params})
	if err = handleError(err, &r); err != nil {
		return
	}
	fmt.Println(json.Unmarshal(r.Result, &template))
	return
}

// GetConnectionCount returns the number of connections to other nodes.
func (b *Bitcoind) GetConnectionCount() (count uint64, err error) {
	r, err := b.client.call("getconnectioncount", nil)
	if err = handleError(err, &r); err != nil {
		return
	}
	count, err = strconv.ParseUint(string(r.Result), 10, 64)
	return
}

// GetDifficulty returns the proof-of-work difficulty as a multiple of
// the minimum difficulty.
func (b *Bitcoind) GetDifficulty() (difficulty float64, err error) {
	r, err := b.client.call("getdifficulty", nil)
	if err = handleError(err, &r); err != nil {
		return
	}
	difficulty, err = strconv.ParseFloat(string(r.Result), 64)
	return
}

// GetInfo return result of "getinfo" command (Amazing !)
func (b *Bitcoind) GetInfo() (i info, err error) {
	r, err := b.client.call("getinfo", nil)
	if err = handleError(err, &r); err != nil {
		return
	}
	err = json.Unmarshal(r.Result, &i)
	return
}

// GetNewAddress return a new address for account [account].
func (b *Bitcoind) GetNewAddress(account ...string) (addr string, err error) {
	// 0 or 1 account
	if len(account) > 1 {
		err = errors.New("Bad parameters for GetNewAddress: you can set 0 or 1 account")
		return
	}
	r, err := b.client.call("getnewaddress", account)
	if err = handleError(err, &r); err != nil {
		return
	}
	err = json.Unmarshal(r.Result, &addr)
	return
}

// GetAddressesByAccount return addresses associated with account <account>
func (b *Bitcoind) GetAddressesByAccount(account string) (addresses []string, err error) {
	r, err := b.client.call("getaddressesbyaccount", []string{account})
	if err = handleError(err, &r); err != nil {
		return
	}
	err = json.Unmarshal(r.Result, &addresses)
	return
}

// walletPassphrase stores the wallet decryption key in memory for <timeout> seconds.
func (b *Bitcoind) WalletPassphrase(passPhrase string, timeout uint64) error {
	r, err := b.client.call("walletpassphrase", []interface{}{passPhrase, timeout})
	return handleError(err, &r)
}
<|endoftext|>


<commit_before>package bms

import (
	"testing"
)

func assert(t *testing.T, b bool) {
	if !b {
		t.Fail()
	}
}

func TestSearch(t *testing.T) {
	haystack := "bokkobokkkobokkkkobokkobokkkobokkkko"
	needle := "bokko"

	assert(t, Search(haystack, needle) == 2)
}

func TestSearchMutibyte(t *testing.T) {
	haystack := "bokkobokkko久保bokkkkobokko久保bokkkobokkkko"
	needle := "久保"

	assert(t, Search(haystack, needle) == 2)
}

func TestEmtpyHaystack(t *testing.T) {
	haystack := ""
	needle := "bokko"

	assert(t, Search(haystack, needle) == 0)
}

func TestEmtpyNeedle(t *testing.T) {
	haystack := "bokko"
	needle := ""

	assert(t, Search(haystack, needle) == 0)
}

func TestShorterHaystackThanNeedle(t *testing.T) {
	haystack := "bokko"
	needle := "bokkko"

	assert(t, Search(haystack, needle) == 0)
}
<commit_msg>add test<commit_after>package bms

import (
	"testing"
)

func assert(t *testing.T, b bool) {
	if !b {
		t.Fail()
	}
}

func TestSearchSuccess(t *testing.T) {
	haystack := "bokkobokkkobokkkkobokkobokkkobokkkko"
	needle := "bokko"

	assert(t, Search(haystack, needle) == 2)
}

func TestSearchFail(t *testing.T) {
	haystack := "bokkobokkkobokkkkobokkobokkkobokkkko"
	needle := "bokkkkko"

	assert(t, Search(haystack, needle) == 0)
}

func TestSearchMutibyte(t *testing.T) {
	haystack := "bokkobokkko久保bokkkkobokko久保bokkkobokkkko"
	needle := "久保"

	assert(t, Search(haystack, needle) == 2)
}

func TestEmtpyHaystack(t *testing.T) {
	haystack := ""
	needle := "bokko"

	assert(t, Search(haystack, needle) == 0)
}

func TestEmtpyNeedle(t *testing.T) {
	haystack := "bokko"
	needle := ""

	assert(t, Search(haystack, needle) == 0)
}

func TestShorterHaystackThanNeedle(t *testing.T) {
	haystack := "bokko"
	needle := "bokkko"

	assert(t, Search(haystack, needle) == 0)
}

func TestSameHaystackAndNeedle(t *testing.T) {
	haystack := "bokko"
	needle := "bokko"

	assert(t, Search(haystack, needle) == 1)
}

func TestSameLengthHaystackAndNeedle(t *testing.T) {
	haystack := "okkob"
	needle := "bokko"

	assert(t, Search(haystack, needle) == 0)
}
<|endoftext|>


<commit_before>package fuse

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"strings"
	"sync/atomic"

	"bazil.org/fuse"
	"bazil.org/fuse/fs"
	"github.com/pachyderm/pachyderm/src/pfs"
	"github.com/pachyderm/pachyderm/src/pfs/pfsutil"
	"golang.org/x/net/context"
)

func Mount(apiClient pfs.ApiClient, repositoryName string, commitID string, mountPoint string) error {
	if err := os.MkdirAll(mountPoint, 0777); err != nil {
		return err
	}
	conn, err := fuse.Mount(
		mountPoint,
		fuse.FSName("pfs://"+repositoryName),
		fuse.Subtype("pfs"),
		fuse.VolumeName("pfs://"+repositoryName),
	)
	if err != nil {
		return err
	}
	defer conn.Close()
	if err := fs.Serve(conn, &filesystem{apiClient, repositoryName, commitID}); err != nil {
		return err
	}

	// check if the mount process has an error to report
	<-conn.Ready
	return conn.MountError
}

type filesystem struct {
	apiClient      pfs.ApiClient
	repositoryName string
	commitID       string
}

func (f *filesystem) Root() (fs.Node, error) {
	return &directory{f, "/"}, nil
}

type directory struct {
	fs   *filesystem
	path string
}

func (*directory) Attr(ctx context.Context, a *fuse.Attr) error {
	log.Print("directory.Attr")
	a.Mode = os.ModeDir | 0555
	return nil
}

func nodeFromFileInfo(fs *filesystem, fileInfo *pfs.FileInfo) (fs.Node, error) {
	if fileInfo == nil {
		return nil, fuse.ENOENT
	}
	switch fileInfo.FileType {
	case pfs.FileType_FILE_TYPE_NONE:
		log.Print("FileType_FILE_TYPE_NONE")
		return nil, fuse.ENOENT
	case pfs.FileType_FILE_TYPE_OTHER:
		log.Print("FileType_FILE_TYPE_OTHER")
		return nil, fuse.ENOENT
	case pfs.FileType_FILE_TYPE_REGULAR:
		return &file{fs, fileInfo.Path.Path, 0, fileInfo.SizeBytes, nil}, nil
	case pfs.FileType_FILE_TYPE_DIR:
		return &directory{fs, fileInfo.Path.Path}, nil
	default:
		return nil, fmt.Errorf("Unrecognized FileType.")
	}
}

func (d *directory) Lookup(ctx context.Context, name string) (fs.Node, error) {
	log.Print("directory.Lookup")
	response, err := pfsutil.GetFileInfo(
		d.fs.apiClient,
		d.fs.repositoryName,
		d.fs.commitID,
		filepath.Join(d.path, name),
	)
	if err != nil {
		log.Print(err)
		return nil, err
	}
	return nodeFromFileInfo(d.fs, response.GetFileInfo())
}

func (d *directory) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
	log.Print("directory.ReadDirAll")
	response, err := pfsutil.ListFiles(d.fs.apiClient, d.fs.repositoryName, d.fs.commitID, d.path, 0, 1)
	if err != nil {
		log.Print(err)
		return nil, err
	}
	var result []fuse.Dirent
	for _, fileInfo := range response.GetFileInfo() {
		shortPath := strings.TrimPrefix(fileInfo.Path.Path, d.path)
		switch fileInfo.FileType {
		case pfs.FileType_FILE_TYPE_NONE:
			continue
		case pfs.FileType_FILE_TYPE_OTHER:
			continue
		case pfs.FileType_FILE_TYPE_REGULAR:
			result = append(result, fuse.Dirent{Name: shortPath, Type: fuse.DT_File})
		case pfs.FileType_FILE_TYPE_DIR:
			result = append(result, fuse.Dirent{Name: shortPath, Type: fuse.DT_Dir})
		default:
			continue
		}
	}
	log.Print(result)
	return result, nil
}

type file struct {
	fs      *filesystem
	path    string
	handles int32
	size    uint64
	reader  io.Reader
}

func (f *file) Attr(ctx context.Context, a *fuse.Attr) error {
	log.Printf("Attr: %#v", f)
	a.Mode = 0666
	a.Size = f.size
	return nil
}

func (f *file) Read(ctx context.Context, request *fuse.ReadRequest, response *fuse.ReadResponse) error {
	log.Printf("Read: %#v", request)
	if f.reader == nil {
		reader, err := pfsutil.GetFile(f.fs.apiClient, f.fs.repositoryName, f.fs.commitID, f.path)
		if err != nil {
			return err
		}
		f.reader = reader
	}
	response.Data = make([]byte, request.Size)
	if _, err := f.reader.Read(response.Data); err != nil {
		return err
	}
	return nil
}

func (f *file) Open(ctx context.Context, request *fuse.OpenRequest, response *fuse.OpenResponse) (fs.Handle, error) {
	log.Printf("Open: %#v", f)
	atomic.AddInt32(&f.handles, 1)
	return f, nil
}

func (f *file) Write(ctx context.Context, request *fuse.WriteRequest, response *fuse.WriteResponse) error {
	log.Printf("Write: %#v", f)
	written, err := pfsutil.PutFile(f.fs.apiClient, f.fs.repositoryName, f.fs.commitID, f.path, bytes.NewReader(request.Data))
	if err != nil {
		return err
	}
	response.Size = written
	return nil
}
<commit_msg>Reading the same file twice now works.<commit_after>package fuse

import (
	"bytes"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"strings"
	"sync/atomic"

	"bazil.org/fuse"
	"bazil.org/fuse/fs"
	"github.com/pachyderm/pachyderm/src/pfs"
	"github.com/pachyderm/pachyderm/src/pfs/pfsutil"
	"golang.org/x/net/context"
)

func Mount(apiClient pfs.ApiClient, repositoryName string, commitID string, mountPoint string) error {
	if err := os.MkdirAll(mountPoint, 0777); err != nil {
		return err
	}
	conn, err := fuse.Mount(
		mountPoint,
		fuse.FSName("pfs://"+repositoryName),
		fuse.Subtype("pfs"),
		fuse.VolumeName("pfs://"+repositoryName),
	)
	if err != nil {
		return err
	}
	defer conn.Close()
	if err := fs.Serve(conn, &filesystem{apiClient, repositoryName, commitID}); err != nil {
		return err
	}

	// check if the mount process has an error to report
	<-conn.Ready
	return conn.MountError
}

type filesystem struct {
	apiClient      pfs.ApiClient
	repositoryName string
	commitID       string
}

func (f *filesystem) Root() (fs.Node, error) {
	return &directory{f, "/"}, nil
}

type directory struct {
	fs   *filesystem
	path string
}

func (*directory) Attr(ctx context.Context, a *fuse.Attr) error {
	log.Print("directory.Attr")
	a.Mode = os.ModeDir | 0555
	return nil
}

func nodeFromFileInfo(fs *filesystem, fileInfo *pfs.FileInfo) (fs.Node, error) {
	if fileInfo == nil {
		return nil, fuse.ENOENT
	}
	switch fileInfo.FileType {
	case pfs.FileType_FILE_TYPE_NONE:
		log.Print("FileType_FILE_TYPE_NONE")
		return nil, fuse.ENOENT
	case pfs.FileType_FILE_TYPE_OTHER:
		log.Print("FileType_FILE_TYPE_OTHER")
		return nil, fuse.ENOENT
	case pfs.FileType_FILE_TYPE_REGULAR:
		return &file{fs, fileInfo.Path.Path, 0, fileInfo.SizeBytes}, nil
	case pfs.FileType_FILE_TYPE_DIR:
		return &directory{fs, fileInfo.Path.Path}, nil
	default:
		return nil, fmt.Errorf("Unrecognized FileType.")
	}
}

func (d *directory) Lookup(ctx context.Context, name string) (fs.Node, error) {
	log.Print("directory.Lookup")
	response, err := pfsutil.GetFileInfo(
		d.fs.apiClient,
		d.fs.repositoryName,
		d.fs.commitID,
		filepath.Join(d.path, name),
	)
	if err != nil {
		log.Print(err)
		return nil, err
	}
	return nodeFromFileInfo(d.fs, response.GetFileInfo())
}

func (d *directory) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
	log.Print("directory.ReadDirAll")
	response, err := pfsutil.ListFiles(d.fs.apiClient, d.fs.repositoryName, d.fs.commitID, d.path, 0, 1)
	if err != nil {
		log.Print(err)
		return nil, err
	}
	var result []fuse.Dirent
	for _, fileInfo := range response.GetFileInfo() {
		shortPath := strings.TrimPrefix(fileInfo.Path.Path, d.path)
		switch fileInfo.FileType {
		case pfs.FileType_FILE_TYPE_NONE:
			continue
		case pfs.FileType_FILE_TYPE_OTHER:
			continue
		case pfs.FileType_FILE_TYPE_REGULAR:
			result = append(result, fuse.Dirent{Name: shortPath, Type: fuse.DT_File})
		case pfs.FileType_FILE_TYPE_DIR:
			result = append(result, fuse.Dirent{Name: shortPath, Type: fuse.DT_Dir})
		default:
			continue
		}
	}
	log.Print(result)
	return result, nil
}

type file struct {
	fs      *filesystem
	path    string
	handles int32
	size    uint64
}

func (f *file) Attr(ctx context.Context, a *fuse.Attr) error {
	log.Printf("Attr: %#v", f)
	a.Mode = 0666
	a.Size = f.size
	return nil
}

func (f *file) Read(ctx context.Context, request *fuse.ReadRequest, response *fuse.ReadResponse) error {
	log.Printf("Read: %#v", request)
	reader, err := pfsutil.GetFile(f.fs.apiClient, f.fs.repositoryName, f.fs.commitID, f.path)
	if err != nil {
		return err
	}
	response.Data = make([]byte, request.Size)
	if _, err := reader.Read(response.Data); err != nil {
		return err
	}
	return nil
}

func (f *file) Open(ctx context.Context, request *fuse.OpenRequest, response *fuse.OpenResponse) (fs.Handle, error) {
	log.Printf("Open: %#v", f)
	atomic.AddInt32(&f.handles, 1)
	return f, nil
}

func (f *file) Write(ctx context.Context, request *fuse.WriteRequest, response *fuse.WriteResponse) error {
	log.Printf("Write: %#v", f)
	written, err := pfsutil.PutFile(f.fs.apiClient, f.fs.repositoryName, f.fs.commitID, f.path, bytes.NewReader(request.Data))
	if err != nil {
		return err
	}
	response.Size = written
	return nil
}
<|endoftext|>


<commit_before>package cic

import (
	"encoding/json"
	"fmt"
	"github.com/tsinghua-io/api-server/resource"
	"io"
	"strconv"
)

type parser interface {
	parse(reader io.Reader, info interface{}, langCode string) error
}

type personalInfoParser struct {
	DataSingle struct {
		Classname string
		Email     string
		Gender    string
		Id        string
		MajorName string
		Name      string
		Phone     string
		Title     string
	}
}

func (p *personalInfoParser) parse(r io.Reader, info interface{}, _ string) error {
	user, ok := info.(*resource.User)
	if !ok {
		return fmt.Errorf("The parser and the destination type do not match.")
	}

	dec := json.NewDecoder(r)
	if err := dec.Decode(p); err != nil {
		return err
	}

	user.Id = p.DataSingle.Id
	user.Name = p.DataSingle.Name
	user.Type = p.DataSingle.Title
	user.Department = p.DataSingle.MajorName
	user.Class = p.DataSingle.Classname
	user.Gender = p.DataSingle.Gender
	user.Email = p.DataSingle.Email
	user.Phone = p.DataSingle.Phone

	return nil
}

type timeLocationParser struct {
	ResultList []struct {
		Skzc string
		Skxq string
		Skjc string
		Skdd string
	}
}

func (p *timeLocationParser) parse(r io.Reader, info interface{}, _ string) error {
	course, ok := info.(*resource.Course)
	if !ok {
		return fmt.Errorf("The parser and the destination type do not match.")
	}

	dec := json.NewDecoder(r)
	if err := dec.Decode(p); err != nil {
		return err
	}
	if len(p.ResultList) == 0 {
		return nil // No time location info
	}

	var err error
	course.Weeks = p.ResultList[0].Skzc
	if course.DayOfWeek, err = strconv.Atoi(p.ResultList[0].Skxq); err != nil {
		return fmt.Errorf("Failed to parse DayOfWeek to int: %s", err)
	}
	if course.PeriodOfDay, err = strconv.Atoi(p.ResultList[0].Skjc); err != nil {
		return fmt.Errorf("Failed to parse PeriodOfDay to int: %s", err)
	}
	course.Location = p.ResultList[0].Skdd

	return nil
}

type assistantsParser struct {
	ResultList []struct {
		id     string
		dwmc   string
		phone  string
		email  string
		name   string
		gender string
	}
}

func (p *assistantsParser) parse(r io.Reader, info interface{}, _ string) error {
	users, ok := info.(*[]*resource.User)
	if !ok {
		return fmt.Errorf("The parser and the destination type do not match.")
	}

	dec := json.NewDecoder(r)
	if err := dec.Decode(p); err != nil {
		return err
	}

	for _, result := range p.ResultList {
		user := &resource.User{
			Id:         result.id,
			Name:       result.name,
			Department: result.dwmc,
			Gender:     result.gender,
			Email:      result.email,
			Phone:      result.phone,
		}
		*users = append(*users, user)
	}

	return nil
}

type coursesParser struct {
	ResultList []struct {
		CourseId      string
		Course_no     string
		Course_seq    string
		Course_name   string
		E_course_name string
		TeacherInfo   struct {
			Id     string
			Name   string
			Email  string
			Phone  string
			Gender string
			Title  string
		}
		CodeDepartmentInfo struct {
			Dwmc   string
			Dwywmc string
		}
		SemesterInfo struct {
			SemesterName  string
			SemesterEname string
		}
		Detail_c    string
		Detail_e    string
		Credit      int
		Course_time int
	}
}

func (p *coursesParser) parse(r io.Reader, info interface{}, langCode string) error {
	courses, ok := info.(*[]*resource.Course)
	if !ok {
		return fmt.Errorf("The parser and the destination type do not match.")
	}

	dec := json.NewDecoder(r)
	if err := dec.Decode(p); err != nil {
		return err
	}

	// TODO: Here we loop through a struct array. Will Go copy every struct?
	// Try some benchmarks.
	for _, result := range p.ResultList {
		// Language specific fields.
		// TODO: Move out of loop?
		var semester, name, description, department string
		switch langCode {
		case "zh-CN":
			semester = result.SemesterInfo.SemesterName
			name = result.Course_name
			description = result.Detail_c
			department = result.CodeDepartmentInfo.Dwmc
		case "en":
			semester = result.SemesterInfo.SemesterEname
			name = result.E_course_name
			description = result.Detail_e
			department = result.CodeDepartmentInfo.Dwywmc
		}

		course := &resource.Course{
			Id:             result.CourseId,
			Semester:       semester,
			CourseNumber:   result.Course_no,
			CourseSequence: result.Course_seq,
			Name:           name,
			Credit:         result.Credit,
			Hour:           result.Course_time,
			Description:    description,

			Teachers: []*resource.User{
				&resource.User{
					Id:         result.TeacherInfo.Id,
					Name:       result.TeacherInfo.Name,
					Type:       result.TeacherInfo.Title,
					Department: department,
					Gender:     result.TeacherInfo.Gender,
					Email:      result.TeacherInfo.Email,
					Phone:      result.TeacherInfo.Phone,
				},
			},
		}
		*courses = append(*courses, course)
	}

	return nil
}

// type announcementsParser struct {
// 	paginationList struct {
// 		recordList []struct {
// 			status       string
// 			courseNotice struct {
// 				id          string
// 				title       string
// 				owner       string
// 				regDate     string
// 				courseId    string
// 				msgPriority string
// 				detail      string
// 			}
// 		}
// 	}
// }

// type filesParser struct {
// }

// type homeworksParser struct {
// 	resultList []struct {
// 		courseHomeworkRecord struct {
// 		}
// 		courseHomeworkInfo struct {
// 		}
// 	}
// }
<commit_msg>Add files & homeworks parsers.<commit_after>package cic

import (
	"encoding/json"
	"fmt"
	"github.com/tsinghua-io/api-server/resource"
	"io"
	"strconv"
)

type parser interface {
	parse(reader io.Reader, info interface{}, langCode string) error
}

type personalInfoParser struct {
	DataSingle struct {
		Classname string
		Email     string
		Gender    string
		Id        string
		MajorName string
		Name      string
		Phone     string
		Title     string
	}
}

func (p *personalInfoParser) parse(r io.Reader, info interface{}, _ string) error {
	user, ok := info.(*resource.User)
	if !ok {
		return fmt.Errorf("The parser and the destination type do not match.")
	}

	dec := json.NewDecoder(r)
	if err := dec.Decode(p); err != nil {
		return err
	}

	user.Id = p.DataSingle.Id
	user.Name = p.DataSingle.Name
	user.Type = p.DataSingle.Title
	user.Department = p.DataSingle.MajorName
	user.Class = p.DataSingle.Classname
	user.Gender = p.DataSingle.Gender
	user.Email = p.DataSingle.Email
	user.Phone = p.DataSingle.Phone

	return nil
}

type timeLocationParser struct {
	ResultList []struct {
		Skzc string
		Skxq string
		Skjc string
		Skdd string
	}
}

func (p *timeLocationParser) parse(r io.Reader, info interface{}, _ string) error {
	course, ok := info.(*resource.Course)
	if !ok {
		return fmt.Errorf("The parser and the destination type do not match.")
	}

	dec := json.NewDecoder(r)
	if err := dec.Decode(p); err != nil {
		return err
	}
	if len(p.ResultList) == 0 {
		return nil // No time location info
	}

	var err error
	course.Weeks = p.ResultList[0].Skzc
	if course.DayOfWeek, err = strconv.Atoi(p.ResultList[0].Skxq); err != nil {
		return fmt.Errorf("Failed to parse DayOfWeek to int: %s", err)
	}
	if course.PeriodOfDay, err = strconv.Atoi(p.ResultList[0].Skjc); err != nil {
		return fmt.Errorf("Failed to parse PeriodOfDay to int: %s", err)
	}
	course.Location = p.ResultList[0].Skdd

	return nil
}

type assistantsParser struct {
	ResultList []struct {
		id     string
		dwmc   string
		phone  string
		email  string
		name   string
		gender string
	}
}

func (p *assistantsParser) parse(r io.Reader, info interface{}, _ string) error {
	users, ok := info.(*[]*resource.User)
	if !ok {
		return fmt.Errorf("The parser and the destination type do not match.")
	}

	dec := json.NewDecoder(r)
	if err := dec.Decode(p); err != nil {
		return err
	}

	for _, result := range p.ResultList {
		user := &resource.User{
			Id:         result.id,
			Name:       result.name,
			Department: result.dwmc,
			Gender:     result.gender,
			Email:      result.email,
			Phone:      result.phone,
		}
		*users = append(*users, user)
	}

	return nil
}

type coursesParser struct {
	ResultList []struct {
		CourseId      string
		Course_no     string
		Course_seq    string
		Course_name   string
		E_course_name string
		TeacherInfo   struct {
			Id     string
			Name   string
			Email  string
			Phone  string
			Gender string
			Title  string
		}
		CodeDepartmentInfo struct {
			Dwmc   string
			Dwywmc string
		}
		SemesterInfo struct {
			SemesterName  string
			SemesterEname string
		}
		Detail_c    string
		Detail_e    string
		Credit      int
		Course_time int
	}
}

func (p *coursesParser) parse(r io.Reader, info interface{}, langCode string) error {
	courses, ok := info.(*[]*resource.Course)
	if !ok {
		return fmt.Errorf("The parser and the destination type do not match.")
	}

	dec := json.NewDecoder(r)
	if err := dec.Decode(p); err != nil {
		return err
	}

	// TODO: Here we loop through a struct array. Will Go copy every struct?
	// Try some benchmarks.
	for _, result := range p.ResultList {
		// Language specific fields.
		// TODO: Move out of loop?
		var semester, name, description, department string
		switch langCode {
		case "zh-CN":
			semester = result.SemesterInfo.SemesterName
			name = result.Course_name
			description = result.Detail_c
			department = result.CodeDepartmentInfo.Dwmc
		case "en":
			semester = result.SemesterInfo.SemesterEname
			name = result.E_course_name
			description = result.Detail_e
			department = result.CodeDepartmentInfo.Dwywmc
		}

		course := &resource.Course{
			Id:             result.CourseId,
			Semester:       semester,
			CourseNumber:   result.Course_no,
			CourseSequence: result.Course_seq,
			Name:           name,
			Credit:         result.Credit,
			Hour:           result.Course_time,
			Description:    description,

			Teachers: []*resource.User{
				&resource.User{
					Id:         result.TeacherInfo.Id,
					Name:       result.TeacherInfo.Name,
					Type:       result.TeacherInfo.Title,
					Department: department,
					Gender:     result.TeacherInfo.Gender,
					Email:      result.TeacherInfo.Email,
					Phone:      result.TeacherInfo.Phone,
				},
			},
		}
		*courses = append(*courses, course)
	}

	return nil
}

type announcementsParser struct {
	PaginationList struct {
		RecordList []struct {
			// Status       string
			CourseNotice struct {
				Id          int64
				Title       string
				Owner       string
				RegDate     string
				CourseId    string
				MsgPriority int
				Detail      string
			}
		}
	}
}

func (p *announcementsParser) parse(r io.Reader, info interface{}, _ string) error {
	announcements, ok := info.(*[]*resource.Announcement)
	if !ok {
		return fmt.Errorf("The parser and the destination type do not match.")
	}

	dec := json.NewDecoder(r)
	if err := dec.Decode(p); err != nil {
		return err
	}

	for _, result := range p.PaginationList.RecordList {
		announcement := &resource.Announcement{
			Id:        string(result.CourseNotice.Id),
			CourseId:  result.CourseNotice.CourseId,
			Title:     result.CourseNotice.Title,
			Owner:     resource.User{Name: result.CourseNotice.Owner},
			CreatedAt: result.CourseNotice.RegDate,
			Priority:  result.CourseNotice.MsgPriority,
			Body:      result.CourseNotice.Detail,
		}
		*announcements = append(*announcements, announcement)
	}

	return nil
}

type filesParser struct {
	resultList map[string]struct {
		teacherInfoView struct {
			nodeName     string
			childMapData map[string]struct {
				title                string
				courseCoursewareList []struct {
					resourcesMappingByFileId struct {
						fileId   string
						regDate  int64
						fileName string
						fileSize string
						userCode string
					}
					regUser string
					title   string
					detail  string
				}
			}
		}
	}
}

type homeworksParser struct {
	ResultList []struct {
		courseHomeworkRecord struct {
			studentId                     string
			teacherId                     string
			homewkId                      string
			regDate                       int64
			homewkDetail                  string
			resourcesMappingByHomewkAffix struct {
				fileId   string
				regDate  string
				fileName string
				fileSize string
				courseId string
				userCode string
			}
			replyDetail string
			// TODO: Add this:
			// resourcesMappingByReplyAffix struct {
			// }
			mark      int
			replyDate int64
			status    string
			ifDelay   string
			gradeUser string
		}
		courseHomeworkInfo struct {
			regDate             int64
			beginDate           int64
			endDate             int64
			title               string
			detail              string
			homewkAffix         string // File ID.
			homewkAffixFilename string
			// answerDetail
			// answerLink
			// answerLinkFilename
			// answerDate
			courseId string
			weiJiao  int
			// yiJiao
			// yiYue
			yiPi   int
			jiaoed int
		}
	}
}
<|endoftext|>


<commit_before>package responseGenerator

import (
	"log"
	"strconv"
	"strings"
	"time"

	"github.com/votinginfoproject/sms-worker/civic_api"
	"github.com/votinginfoproject/sms-worker/data"
	"github.com/votinginfoproject/sms-worker/response_generator/elo"
	"github.com/votinginfoproject/sms-worker/response_generator/polling_location"
	"github.com/votinginfoproject/sms-worker/response_generator/registration"
	"github.com/votinginfoproject/sms-worker/responses"
	"github.com/votinginfoproject/sms-worker/users"
)

type Generator struct {
	civic    civicApi.Querier
	content  *responses.Content
	triggers map[string]map[string]string
	userDb   *users.Db
}

func New(civic civicApi.Querier, userDb *users.Db) *Generator {
	rawContent, err := data.Asset("raw/data.yml")
	if err != nil {
		log.Panic("[ERROR] Failed to load responses : ", err)
	}

	content, triggers := responses.Load(rawContent)
	return &Generator{civic, content, triggers, userDb}
}

func (r *Generator) Generate(number string, message string, routine int) []string {
	user, err := r.userDb.GetOrCreate(number)
	if err != nil {
		log.Printf("[ERROR] [%d] User store error : %s", routine, err)
		return []string{r.content.Errors.Text["en"]["generalBackend"]}
	}

	message = strings.TrimSpace(message)
	message = strings.ToLower(message)

	action := r.triggers[user.Language][message]

	if len(action) == 0 {
		success, newLanguage := r.checkIfOtherLanguage(message)
		if success == true {
			user.Language = newLanguage
			action = "ChangeLanguage"
		}
	}

	log.Printf("[INFO] [%d] Taking action '%s'", routine, action)

	lctm := r.lastContactTimeMessage(user)

	messages := r.performAction(action, user, message, routine)

	if len(lctm) > 0 {
		messages = append(messages, lctm)
	}

	return messages
}

func (r *Generator) lastContactTimeMessage(user *users.User) string {
	message := ""

	lcInt, _ := strconv.ParseInt(user.LastContactTime, 10, 64)
	lcTime := time.Unix(lcInt, 0)
	duration := time.Since(lcTime)

	if duration > (7*24*time.Hour) && len(user.Data["address"]) > 0 {
		message = r.content.LastContact.Text[user.Language]["prefix"] + "\n" + user.Data["address"]
	}

	return message
}

func (r *Generator) performAction(action string, user *users.User, message string, routine int) []string {
	var messages []string

	switch action {
	case "Elo":
		messages = r.elo(user, routine)
	case "Registration":
		messages = r.registration(user, routine)
	case "Help":
		if user.FirstContact == true {
			messages = []string{r.content.Intro.Text[user.Language]["all"]}
		} else {
			messages = []string{r.content.Help.Text[user.Language]["menu"], r.content.Help.Text[user.Language]["languages"]}
		}
	case "About":
		if user.FirstContact == true {
			messages = []string{r.content.Intro.Text[user.Language]["all"]}
		} else {
			messages = []string{r.content.About.Text[user.Language]["all"]}
		}
	case "Intro":
		messages = []string{r.content.Intro.Text[user.Language]["all"]}
	case "ChangeLanguage":
		messages = r.changeLanguage(user)
	case "PollingLocation":
		if len(user.Data["address"]) == 0 && user.FirstContact == true {
			messages = []string{r.content.Intro.Text[user.Language]["all"]}
		} else if len(user.Data["address"]) == 0 && user.FirstContact == false {
			messages = []string{r.content.Errors.Text[user.Language]["needAddress"] + "\n\n" + r.content.Help.Text[user.Language]["languages"]}
		} else {
			messages = r.pollingLocation(user, user.Data["address"], routine)
		}
	default:
		messages = r.pollingLocation(user, message, routine)
	}

	return messages
}

func (r *Generator) checkIfOtherLanguage(message string) (bool, string) {
	for language, _ := range r.triggers {
		if len(r.triggers[language][message]) > 0 {
			return true, language
		}
	}

	return false, ""
}

func (r *Generator) changeLanguage(user *users.User) []string {
	err := r.userDb.ChangeLanguage(user.Data["phone_number"], user.Language)
	if err != nil {
		return []string{r.content.Errors.Text[user.Language]["generalBackend"]}
	}

	return []string{r.content.Help.Text[user.Language]["menu"], r.content.Help.Text[user.Language]["languages"]}
}

func (r *Generator) elo(user *users.User, routine int) []string {
	if len(user.Data["address"]) == 0 {
		if user.FirstContact == true {
			return []string{r.content.Intro.Text[user.Language]["all"]}
		} else {
			return []string{r.content.Errors.Text[user.Language]["needAddress"] + "\n\n" + r.content.Help.Text[user.Language]["languages"]}
		}
	}

	res, err := r.civic.Query(user.Data["address"])
	if err != nil {
		log.Printf("[ERROR] [%d] Civic API failure : %s", routine, err)
		return []string{r.content.Errors.Text[user.Language]["generalBackend"]}
	}

	return elo.BuildMessage(res, user.Language, r.content)
}

func (r *Generator) registration(user *users.User, routine int) []string {
	if len(user.Data["address"]) == 0 {
		if user.FirstContact == true {
			return []string{r.content.Intro.Text[user.Language]["all"]}
		} else {
			return []string{r.content.Errors.Text[user.Language]["needAddress"] + "\n\n" + r.content.Help.Text[user.Language]["languages"]}
		}
	}

	res, err := r.civic.Query(user.Data["address"])
	if err != nil {
		log.Printf("[ERROR] [%d] Civic API failure : %s", routine, err)
		return []string{r.content.Errors.Text[user.Language]["generalBackend"]}
	}

	return registration.BuildMessage(res, user.Language, r.content)
}

func (r *Generator) pollingLocation(user *users.User, message string, routine int) []string {
	newUser := false
	if len(user.Data["address"]) == 0 {
		newUser = true
	}

	res, err := r.civic.Query(message)
	if err != nil {
		log.Printf("[ERROR] [%d] Civic API failure : %s", routine, err)
		return []string{r.content.Errors.Text[user.Data["language"]]["generalBackend"]}
	}

	messages, success := pollingLocation.BuildMessage(res, user.Data["language"], newUser, user.FirstContact, r.content)
	if success == true {
		r.userDb.SetAddress(user.Data["phone_number"], message)
	}

	return messages
}
<commit_msg>pollingLocation cleanup<commit_after>package responseGenerator

import (
	"log"
	"strconv"
	"strings"
	"time"

	"github.com/votinginfoproject/sms-worker/civic_api"
	"github.com/votinginfoproject/sms-worker/data"
	"github.com/votinginfoproject/sms-worker/response_generator/elo"
	"github.com/votinginfoproject/sms-worker/response_generator/polling_location"
	"github.com/votinginfoproject/sms-worker/response_generator/registration"
	"github.com/votinginfoproject/sms-worker/responses"
	"github.com/votinginfoproject/sms-worker/users"
)

type Generator struct {
	civic    civicApi.Querier
	content  *responses.Content
	triggers map[string]map[string]string
	userDb   *users.Db
}

func New(civic civicApi.Querier, userDb *users.Db) *Generator {
	rawContent, err := data.Asset("raw/data.yml")
	if err != nil {
		log.Panic("[ERROR] Failed to load responses : ", err)
	}

	content, triggers := responses.Load(rawContent)
	return &Generator{civic, content, triggers, userDb}
}

func (r *Generator) Generate(number string, message string, routine int) []string {
	user, err := r.userDb.GetOrCreate(number)
	if err != nil {
		log.Printf("[ERROR] [%d] User store error : %s", routine, err)
		return []string{r.content.Errors.Text["en"]["generalBackend"]}
	}

	message = strings.TrimSpace(message)
	message = strings.ToLower(message)

	action := r.triggers[user.Language][message]

	if len(action) == 0 {
		success, newLanguage := r.checkIfOtherLanguage(message)
		if success == true {
			user.Language = newLanguage
			action = "ChangeLanguage"
		}
	}

	log.Printf("[INFO] [%d] Taking action '%s'", routine, action)

	lctm := r.lastContactTimeMessage(user)

	messages := r.performAction(action, user, message, routine)

	if len(lctm) > 0 {
		messages = append(messages, lctm)
	}

	return messages
}

func (r *Generator) lastContactTimeMessage(user *users.User) string {
	message := ""

	lcInt, _ := strconv.ParseInt(user.LastContactTime, 10, 64)
	lcTime := time.Unix(lcInt, 0)
	duration := time.Since(lcTime)

	if duration > (7*24*time.Hour) && len(user.Data["address"]) > 0 {
		message = r.content.LastContact.Text[user.Language]["prefix"] + "\n" + user.Data["address"]
	}

	return message
}

func (r *Generator) performAction(action string, user *users.User, message string, routine int) []string {
	var messages []string

	switch action {
	case "Elo":
		messages = r.elo(user, routine)
	case "Registration":
		messages = r.registration(user, routine)
	case "Help":
		if user.FirstContact == true {
			messages = []string{r.content.Intro.Text[user.Language]["all"]}
		} else {
			messages = []string{r.content.Help.Text[user.Language]["menu"], r.content.Help.Text[user.Language]["languages"]}
		}
	case "About":
		if user.FirstContact == true {
			messages = []string{r.content.Intro.Text[user.Language]["all"]}
		} else {
			messages = []string{r.content.About.Text[user.Language]["all"]}
		}
	case "Intro":
		messages = []string{r.content.Intro.Text[user.Language]["all"]}
	case "ChangeLanguage":
		messages = r.changeLanguage(user)
	case "PollingLocation":
		if len(user.Data["address"]) == 0 && user.FirstContact == true {
			messages = []string{r.content.Intro.Text[user.Language]["all"]}
		} else if len(user.Data["address"]) == 0 && user.FirstContact == false {
			messages = []string{r.content.Errors.Text[user.Language]["needAddress"] + "\n\n" + r.content.Help.Text[user.Language]["languages"]}
		} else {
			messages = r.pollingLocation(user, user.Data["address"], routine)
		}
	default:
		messages = r.pollingLocation(user, message, routine)
	}

	return messages
}

func (r *Generator) checkIfOtherLanguage(message string) (bool, string) {
	for language, _ := range r.triggers {
		if len(r.triggers[language][message]) > 0 {
			return true, language
		}
	}

	return false, ""
}

func (r *Generator) changeLanguage(user *users.User) []string {
	err := r.userDb.ChangeLanguage(user.Data["phone_number"], user.Language)
	if err != nil {
		return []string{r.content.Errors.Text[user.Language]["generalBackend"]}
	}

	return []string{r.content.Help.Text[user.Language]["menu"], r.content.Help.Text[user.Language]["languages"]}
}

func (r *Generator) elo(user *users.User, routine int) []string {
	if len(user.Data["address"]) == 0 {
		if user.FirstContact == true {
			return []string{r.content.Intro.Text[user.Language]["all"]}
		} else {
			return []string{r.content.Errors.Text[user.Language]["needAddress"] + "\n\n" + r.content.Help.Text[user.Language]["languages"]}
		}
	}

	res, err := r.civic.Query(user.Data["address"])
	if err != nil {
		log.Printf("[ERROR] [%d] Civic API failure : %s", routine, err)
		return []string{r.content.Errors.Text[user.Language]["generalBackend"]}
	}

	return elo.BuildMessage(res, user.Language, r.content)
}

func (r *Generator) registration(user *users.User, routine int) []string {
	if len(user.Data["address"]) == 0 {
		if user.FirstContact == true {
			return []string{r.content.Intro.Text[user.Language]["all"]}
		} else {
			return []string{r.content.Errors.Text[user.Language]["needAddress"] + "\n\n" + r.content.Help.Text[user.Language]["languages"]}
		}
	}

	res, err := r.civic.Query(user.Data["address"])
	if err != nil {
		log.Printf("[ERROR] [%d] Civic API failure : %s", routine, err)
		return []string{r.content.Errors.Text[user.Language]["generalBackend"]}
	}

	return registration.BuildMessage(res, user.Language, r.content)
}

func (r *Generator) pollingLocation(user *users.User, message string, routine int) []string {
	newUser := false
	if len(user.Data["address"]) == 0 {
		newUser = true
	}

	res, err := r.civic.Query(message)
	if err != nil {
		log.Printf("[ERROR] [%d] Civic API failure : %s", routine, err)
		return []string{r.content.Errors.Text[user.Language]["generalBackend"]}
	}

	messages, success := pollingLocation.BuildMessage(res, user.Language, newUser, user.FirstContact, r.content)
	if success == true {
		r.userDb.SetAddress(user.Data["phone_number"], message)
	}

	return messages
}
<|endoftext|>


<commit_before>package colony

import (
	"testing"
	"time"

	"github.com/aaasen/colony/graph"
)

func TestColony(t *testing.T) {
	tickDuration := time.Second

	colony := graph.NewLabeledGraph()

	to1 := make(chan *Resource, 3)
	to2 := make(chan *Resource, 3)
	to3 := make(chan *Resource, 3)

	node1 := NewChannelNode(to1, time.Tick(tickDuration))
	node2 := NewChannelNode(to2, time.Tick(tickDuration))
	node3 := NewChannelNode(to3, time.Tick(tickDuration))

	node1.AddEdge(NewChannelEdge(node2, to2))
	node1.AddEdge(NewChannelEdge(node3, to3))
	node2.AddEdge(NewChannelEdge(node1, to1))
	node2.AddEdge(NewChannelEdge(node3, to3))
	node3.AddEdge(NewChannelEdge(node1, to1))
	node3.AddEdge(NewChannelEdge(node2, to2))

	colony.AddNode("node1", node1)
	colony.AddNode("node2", node2)
	colony.AddNode("node3", node3)

	go node1.Listen()
	go node2.Listen()
	go node3.Listen()

	if edge, ok := node1.Edges[0].(*ChannelEdge); ok {
		edge.Resources <- NewResource(10.0)
	}

	time.Sleep(time.Second * 10)
}
<commit_msg>adds a factory to unit tests<commit_after>package colony

import (
	"testing"
	"time"

	"github.com/aaasen/colony/graph"
)

func TestColony(t *testing.T) {
	tickDuration := time.Second

	colony := graph.NewLabeledGraph()

	to1 := make(chan *Resource, 3)
	to2 := make(chan *Resource, 3)
	to3 := make(chan *Resource, 3)

	node1 := NewFactoryNode(to1, time.Tick(tickDuration), 10.0)
	node2 := NewChannelNode(to2, time.Tick(tickDuration))
	node3 := NewChannelNode(to3, time.Tick(tickDuration))

	node1.AddEdge(NewChannelEdge(node2, to2))
	node1.AddEdge(NewChannelEdge(node3, to3))
	node2.AddEdge(NewChannelEdge(node1, to1))
	node2.AddEdge(NewChannelEdge(node3, to3))
	node3.AddEdge(NewChannelEdge(node1, to1))
	node3.AddEdge(NewChannelEdge(node2, to2))

	colony.AddNode("node1", node1)
	colony.AddNode("node2", node2)
	colony.AddNode("node3", node3)

	go node1.Listen()
	go node2.Listen()
	go node3.Listen()

	if edge, ok := node1.Edges[0].(*ChannelEdge); ok {
		edge.Resources <- NewResource(10.0)
	}

	time.Sleep(time.Second * 10)
}
<|endoftext|>


<commit_before>package cli

import (
	"errors"
	"fmt"
	"os"
	"strings"

	cmds "github.com/jbenet/go-ipfs/commands"
)

// Parse parses the input commandline string (cmd, flags, and args).
// returns the corresponding command Request object.
// Multiple root commands are supported:
// Parse will search each root to find the one that best matches the requested subcommand.
func Parse(input []string, roots ...*cmds.Command) (cmds.Request, *cmds.Command, *cmds.Command, []string, error) {
	var root, cmd *cmds.Command
	var path, stringArgs []string
	var opts map[string]interface{}

	// use the root that matches the longest path (most accurately matches request)
	maxLength := 0
	for _, root2 := range roots {
		path2, input2, cmd2 := parsePath(input, root2)
		opts2, stringArgs2, err := parseOptions(input2)
		if err != nil {
			return nil, root, cmd2, path2, err
		}

		length := len(path2)
		if length > maxLength {
			maxLength = length
			root = root2
			path = path2
			cmd = cmd2
			opts = opts2
			stringArgs = stringArgs2
		}
	}

	if maxLength == 0 {
		return nil, root, nil, path, errors.New("Not a valid subcommand")
	}

	args, err := parseArgs(stringArgs, cmd)
	if err != nil {
		return nil, root, cmd, path, err
	}

	optDefs, err := root.GetOptions(path)
	if err != nil {
		return nil, root, cmd, path, err
	}

	req := cmds.NewRequest(path, opts, args, cmd, optDefs)

	err = cmd.CheckArguments(req)
	if err != nil {
		return req, root, cmd, path, err
	}

	return req, root, cmd, path, nil
}

// parsePath separates the command path and the opts and args from a command string
// returns command path slice, rest slice, and the corresponding *cmd.Command
func parsePath(input []string, root *cmds.Command) ([]string, []string, *cmds.Command) {
	cmd := root
	i := 0

	for _, blob := range input {
		if strings.HasPrefix(blob, "-") {
			break
		}

		sub := cmd.Subcommand(blob)
		if sub == nil {
			break
		}
		cmd = sub

		i++
	}

	return input[:i], input[i:], cmd
}

// parseOptions parses the raw string values of the given options
// returns the parsed options as strings, along with the CLI args
func parseOptions(input []string) (map[string]interface{}, []string, error) {
	opts := make(map[string]interface{})
	args := []string{}

	for i := 0; i < len(input); i++ {
		blob := input[i]

		if strings.HasPrefix(blob, "-") {
			name := blob[1:]
			value := ""

			// support single and double dash
			if strings.HasPrefix(name, "-") {
				name = name[1:]
			}

			if strings.Contains(name, "=") {
				split := strings.SplitN(name, "=", 2)
				name = split[0]
				value = split[1]
			}

			if _, ok := opts[name]; ok {
				return nil, nil, fmt.Errorf("Duplicate values for option '%s'", name)
			}

			opts[name] = value

		} else {
			args = append(args, blob)
		}
	}

	return opts, args, nil
}

func parseArgs(stringArgs []string, cmd *cmds.Command) ([]interface{}, error) {
	args := make([]interface{}, 0)

	// count required argument definitions
	lenRequired := 0
	for _, argDef := range cmd.Arguments {
		if argDef.Required {
			lenRequired++
		}
	}

	valueIndex := 0 // the index of the current stringArgs value
	for _, argDef := range cmd.Arguments {
		// skip optional argument definitions if there aren't sufficient remaining values
		if len(stringArgs)-valueIndex <= lenRequired && !argDef.Required {
			continue
		} else if argDef.Required {
			lenRequired--
		}

		if valueIndex >= len(stringArgs) {
			break
		}

		if argDef.Variadic {
			for _, arg := range stringArgs[valueIndex:] {
				var err error
				args, err = appendArg(args, argDef, arg)
				if err != nil {
					return nil, err
				}
				valueIndex++
			}
		} else {
			var err error
			args, err = appendArg(args, argDef, stringArgs[valueIndex])
			if err != nil {
				return nil, err
			}
			valueIndex++
		}
	}

	if len(stringArgs)-valueIndex > 0 {
		args = append(args, make([]interface{}, len(stringArgs)-valueIndex))
	}

	return args, nil
}

func appendArg(args []interface{}, argDef cmds.Argument, value string) ([]interface{}, error) {
	if argDef.Type == cmds.ArgString {
		return append(args, value), nil

	} else {
		in, err := os.Open(value)
		if err != nil {
			return nil, err
		}
		return append(args, in), nil
	}
}
<commit_msg>add urgent todo. fix before merge<commit_after>package cli

import (
	"errors"
	"fmt"
	"os"
	"strings"

	cmds "github.com/jbenet/go-ipfs/commands"
)

// Parse parses the input commandline string (cmd, flags, and args).
// returns the corresponding command Request object.
// Multiple root commands are supported:
// Parse will search each root to find the one that best matches the requested subcommand.
func Parse(input []string, roots ...*cmds.Command) (cmds.Request, *cmds.Command, *cmds.Command, []string, error) {
	var root, cmd *cmds.Command
	var path, stringArgs []string
	var opts map[string]interface{}

	// use the root that matches the longest path (most accurately matches request)
	maxLength := 0
	for _, root2 := range roots {
		path2, input2, cmd2 := parsePath(input, root2)
		opts2, stringArgs2, err := parseOptions(input2)
		if err != nil {
			return nil, root, cmd2, path2, err
		}

		length := len(path2)
		if length > maxLength {
			maxLength = length
			root = root2
			path = path2
			cmd = cmd2
			opts = opts2
			stringArgs = stringArgs2
		}
	}

	if maxLength == 0 {
		return nil, root, nil, path, errors.New("Not a valid subcommand")
	}

	args, err := parseArgs(stringArgs, cmd)
	if err != nil {
		return nil, root, cmd, path, err
	}

	optDefs, err := root.GetOptions(path)
	if err != nil {
		return nil, root, cmd, path, err
	}

	req := cmds.NewRequest(path, opts, args, cmd, optDefs)

	err = cmd.CheckArguments(req)
	if err != nil {
		return req, root, cmd, path, err
	}

	return req, root, cmd, path, nil
}

// parsePath separates the command path and the opts and args from a command string
// returns command path slice, rest slice, and the corresponding *cmd.Command
func parsePath(input []string, root *cmds.Command) ([]string, []string, *cmds.Command) {
	cmd := root
	i := 0

	for _, blob := range input {
		if strings.HasPrefix(blob, "-") {
			break
		}

		sub := cmd.Subcommand(blob)
		if sub == nil {
			break
		}
		cmd = sub

		i++
	}

	return input[:i], input[i:], cmd
}

// parseOptions parses the raw string values of the given options
// returns the parsed options as strings, along with the CLI args
func parseOptions(input []string) (map[string]interface{}, []string, error) {
	opts := make(map[string]interface{})
	args := []string{}

	for i := 0; i < len(input); i++ {
		blob := input[i]

		if strings.HasPrefix(blob, "-") {
			name := blob[1:]
			value := ""

			// support single and double dash
			if strings.HasPrefix(name, "-") {
				name = name[1:]
			}

			if strings.Contains(name, "=") {
				split := strings.SplitN(name, "=", 2)
				name = split[0]
				value = split[1]
			}

			if _, ok := opts[name]; ok {
				return nil, nil, fmt.Errorf("Duplicate values for option '%s'", name)
			}

			opts[name] = value

		} else {
			args = append(args, blob)
		}
	}

	return opts, args, nil
}

func parseArgs(stringArgs []string, cmd *cmds.Command) ([]interface{}, error) {
	args := make([]interface{}, 0)

	// count required argument definitions
	lenRequired := 0
	for _, argDef := range cmd.Arguments {
		if argDef.Required {
			lenRequired++
		}
	}

	valueIndex := 0 // the index of the current stringArgs value
	for _, argDef := range cmd.Arguments {
		// skip optional argument definitions if there aren't sufficient remaining values
		if len(stringArgs)-valueIndex <= lenRequired && !argDef.Required {
			continue
		} else if argDef.Required {
			lenRequired--
		}

		if valueIndex >= len(stringArgs) {
			break
		}

		if argDef.Variadic {
			for _, arg := range stringArgs[valueIndex:] {
				var err error
				args, err = appendArg(args, argDef, arg)
				if err != nil {
					return nil, err
				}
				valueIndex++
			}
		} else {
			var err error
			args, err = appendArg(args, argDef, stringArgs[valueIndex])
			if err != nil {
				return nil, err
			}
			valueIndex++
		}
	}

	if len(stringArgs)-valueIndex > 0 {
		args = append(args, make([]interface{}, len(stringArgs)-valueIndex))
	}

	return args, nil
}

func appendArg(args []interface{}, argDef cmds.Argument, value string) ([]interface{}, error) {
	if argDef.Type == cmds.ArgString {
		return append(args, value), nil

	} else {
		in, err := os.Open(value) // FIXME(btc) must close file. fix before merge
		if err != nil {
			return nil, err
		}
		return append(args, in), nil
	}
}
<|endoftext|>


<commit_before>package common

import (
	"bytes"
	"fmt"
	"reflect"
	"testing"
	"time"
)

func TestIsEnabled(t *testing.T) {
	t.Parallel()
	expected := "Enabled"
	actual := IsEnabled(true)
	if actual != expected {
		t.Error(fmt.Sprintf("Test failed. Expected %s. Actual %s", expected, actual))
	}

	expected = "Disabled"
	actual = IsEnabled(false)
	if actual != expected {
		t.Error(fmt.Sprintf("Test failed. Expected %s. Actual %s", expected, actual))
	}
}

func TestGetMD5(t *testing.T) {
	t.Parallel()
	var originalString = []byte("I am testing the MD5 function in common!")
	var expectedOutput = []byte("18fddf4a41ba90a7352765e62e7a8744")
	actualOutput := GetMD5(originalString)
	actualStr := HexEncodeToString(actualOutput)
	if !bytes.Equal(expectedOutput, []byte(actualStr)) {
		t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'", expectedOutput, []byte(actualStr)))
	}

}

func TestGetSHA512(t *testing.T) {
	t.Parallel()
	var originalString = []byte("I am testing the GetSHA512 function in common!")
	var expectedOutput = []byte("a2273f492ea73fddc4f25c267b34b3b74998bd8a6301149e1e1c835678e3c0b90859fce22e4e7af33bde1711cbb924809aedf5d759d648d61774b7185c5dc02b")
	actualOutput := GetSHA512(originalString)
	actualStr := HexEncodeToString(actualOutput)
	if !bytes.Equal(expectedOutput, []byte(actualStr)) {
		t.Error(fmt.Sprintf("Test failed. Expected '%x'. Actual '%x'", expectedOutput, []byte(actualStr)))
	}
}

func TestGetSHA256(t *testing.T) {
	t.Parallel()
	var originalString = []byte("I am testing the GetSHA256 function in common!")
	var expectedOutput = []byte("0962813d7a9f739cdcb7f0c0be0c2a13bd630167e6e54468266e4af6b1ad9303")
	actualOutput := GetSHA256(originalString)
	actualStr := HexEncodeToString(actualOutput)
	if !bytes.Equal(expectedOutput, []byte(actualStr)) {
		t.Error(fmt.Sprintf("Test failed. Expected '%x'. Actual '%x'", expectedOutput, []byte(actualStr)))
	}
}

func TestStringToLower(t *testing.T) {
	t.Parallel()
	upperCaseString := "HEY MAN"
	expectedResult := "hey man"
	actualResult := StringToLower(upperCaseString)
	if actualResult != expectedResult {
		t.Error("...")
	}
}

func TestStringToUpper(t *testing.T) {
	t.Parallel()
	upperCaseString := "hey man"
	expectedResult := "HEY MAN"
	actualResult := StringToUpper(upperCaseString)
	if actualResult != expectedResult {
		t.Error("...")
	}
}

func TestHexEncodeToString(t *testing.T) {
	t.Parallel()
	originalInput := []byte("string")
	expectedOutput := "737472696e67"
	actualResult := HexEncodeToString(originalInput)
	if actualResult != expectedOutput {
		t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'", expectedOutput, actualResult))
	}
}

func TestBase64Decode(t *testing.T) {
	t.Parallel()
	originalInput := "aGVsbG8="
	expectedOutput := []byte("hello")
	actualResult, err := Base64Decode(originalInput)
	if !bytes.Equal(actualResult, expectedOutput) {
		t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'. Error: %s", expectedOutput, actualResult, err))
	}
}

func TestBase64Encode(t *testing.T) {
	t.Parallel()
	originalInput := []byte("hello")
	expectedOutput := "aGVsbG8="
	actualResult := Base64Encode(originalInput)
	if actualResult != expectedOutput {
		t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'", expectedOutput, actualResult))
	}
}

func TestStringSliceDifference(t *testing.T) {
	t.Parallel()
	originalInputOne := []string{"hello"}
	originalInputTwo := []string{"moto"}
	expectedOutput := []string{"hello moto"}
	actualResult := StringSliceDifference(originalInputOne, originalInputTwo)
	if reflect.DeepEqual(expectedOutput, actualResult) {
		t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'", expectedOutput, actualResult))
	}
}

func TestStringContains(t *testing.T) {
	t.Parallel()
	originalInput := "hello"
	originalInputSubstring := "he"
	expectedOutput := true
	actualResult := StringContains(originalInput, originalInputSubstring)
	if actualResult != expectedOutput {
		t.Error(fmt.Sprintf("Test failed. Expected '%t'. Actual '%t'", expectedOutput, actualResult))
	}
}

func TestJoinStrings(t *testing.T) {
	t.Parallel()
	originalInputOne := []string{"hello", "moto"}
	seperator := ","
	expectedOutput := "hello,moto"
	actualResult := JoinStrings(originalInputOne, seperator)
	if expectedOutput != actualResult {
		t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'", expectedOutput, actualResult))
	}
}

func TestSplitStrings(t *testing.T) {
	t.Parallel()
	originalInputOne := "hello,moto"
	seperator := ","
	expectedOutput := []string{"hello", "moto"}
	actualResult := SplitStrings(originalInputOne, seperator)
	if !reflect.DeepEqual(expectedOutput, actualResult) {
		t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'", expectedOutput, actualResult))
	}
}

func TestRoundFloat(t *testing.T) {
	t.Parallel()
	originalInput := float64(1.4545445445)
	precisionInput := 2
	expectedOutput := float64(1.45)
	actualResult := RoundFloat(originalInput, precisionInput)
	if expectedOutput != actualResult {
		t.Error(fmt.Sprintf("Test failed. Expected '%f'. Actual '%f'.", expectedOutput, actualResult))
	}
}

func TestCalculateFee(t *testing.T) {
	t.Parallel()
	originalInput := float64(1)
	fee := float64(1)
	expectedOutput := float64(0.01)
	actualResult := CalculateFee(originalInput, fee)
	if expectedOutput != actualResult {
		t.Error(fmt.Sprintf("Test failed. Expected '%f'. Actual '%f'.", expectedOutput, actualResult))
	}
}

func TestCalculateAmountWithFee(t *testing.T) {
	t.Parallel()
	originalInput := float64(1)
	fee := float64(1)
	expectedOutput := float64(1.01)
	actualResult := CalculateAmountWithFee(originalInput, fee)
	if expectedOutput != actualResult {
		t.Error(fmt.Sprintf("Test failed. Expected '%f'. Actual '%f'.", expectedOutput, actualResult))
	}
}

func TestCalculatePercentageGainOrLoss(t *testing.T) {
	t.Parallel()
	originalInput := float64(9300)
	secondInput := float64(9000)
	expectedOutput := 3.3333333333333335
	actualResult := CalculatePercentageGainOrLoss(originalInput, secondInput)
	if expectedOutput != actualResult {
		t.Error(fmt.Sprintf("Test failed. Expected '%f'. Actual '%f'.", expectedOutput, actualResult))
	}
}

func TestCalculatePercentageDifference(t *testing.T) {
	t.Parallel()
	originalInput := float64(10)
	secondAmount := float64(5)
	expectedOutput := 66.66666666666666
	actualResult := CalculatePercentageDifference(originalInput, secondAmount)
	if expectedOutput != actualResult {
		t.Error(fmt.Sprintf("Test failed. Expected '%f'. Actual '%f'.", expectedOutput, actualResult))
	}
}

func TestCalculateNetProfit(t *testing.T) {
	t.Parallel()
	amount := float64(5)
	priceThen := float64(1)
	priceNow := float64(10)
	costs := float64(1)
	expectedOutput := float64(44)
	actualResult := CalculateNetProfit(amount, priceThen, priceNow, costs)
	if expectedOutput != actualResult {
		t.Error(fmt.Sprintf("Test failed. Expected '%f'. Actual '%f'.", expectedOutput, actualResult))
	}
}

func TestExtractHost(t *testing.T) {
	t.Parallel()
	address := "localhost:1337"
	expectedOutput := "localhost"
	actualResult := ExtractHost(address)
	if expectedOutput != actualResult {
		t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'.", expectedOutput, actualResult))
	}

	address = "192.168.1.100:1337"
	expectedOutput = "192.168.1.100"
	actualResult = ExtractHost(address)
	if expectedOutput != actualResult {
		t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'.", expectedOutput, actualResult))
	}
}

func TestExtractPort(t *testing.T) {
	t.Parallel()
	address := "localhost:1337"
	expectedOutput := 1337
	actualResult := ExtractPort(address)
	if expectedOutput != actualResult {
		t.Error(fmt.Sprintf("Test failed. Expected '%d'. Actual '%d'.", expectedOutput, actualResult))
	}
}

func TestUnixTimestampToTime(t *testing.T) {
	t.Parallel()
	testTime := int64(1489439831)
	tm := time.Unix(testTime, 0)
	expectedOutput := "2017-03-13 21:17:11 +0000 UTC"
	actualResult := UnixTimestampToTime(testTime)
	if tm.String() != actualResult.String() {
		t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'.", expectedOutput, actualResult))
	}
}

func TestUnixTimestampStrToTime(t *testing.T) {
	t.Parallel()
	testTime := "1489439831"
	expectedOutput := "2017-03-13 21:17:11 +0000 UTC"
	actualResult, err := UnixTimestampStrToTime(testTime)
	if err != nil {
		t.Error(err)
	}

	if actualResult.UTC().String() != expectedOutput {
		t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'.", expectedOutput, actualResult))
	}
}

func TestGetURIPath(t *testing.T) {
	t.Parallel()
	// mapping of input vs expected result
	testTable := map[string]string{
		"https://api.gdax.com/accounts":         "/accounts",
		"https://api.gdax.com/accounts?a=1&b=2": "/accounts?a=1&b=2",
		"ht:tp:/invalidurl":                     "",
	}
	for testInput, expectedOutput := range testTable {
		actualOutput := GetURIPath(testInput)
		if actualOutput != expectedOutput {
			t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'.",
				expectedOutput, actualOutput))
		}
	}
}
<commit_msg>Added test for DataContains function in common.go<commit_after>package common

import (
	"bytes"
	"fmt"
	"reflect"
	"testing"
	"time"
)

func TestIsEnabled(t *testing.T) {
	t.Parallel()
	expected := "Enabled"
	actual := IsEnabled(true)
	if actual != expected {
		t.Error(fmt.Sprintf("Test failed. Expected %s. Actual %s", expected, actual))
	}

	expected = "Disabled"
	actual = IsEnabled(false)
	if actual != expected {
		t.Error(fmt.Sprintf("Test failed. Expected %s. Actual %s", expected, actual))
	}
}

func TestGetMD5(t *testing.T) {
	t.Parallel()
	var originalString = []byte("I am testing the MD5 function in common!")
	var expectedOutput = []byte("18fddf4a41ba90a7352765e62e7a8744")
	actualOutput := GetMD5(originalString)
	actualStr := HexEncodeToString(actualOutput)
	if !bytes.Equal(expectedOutput, []byte(actualStr)) {
		t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'", expectedOutput, []byte(actualStr)))
	}

}

func TestGetSHA512(t *testing.T) {
	t.Parallel()
	var originalString = []byte("I am testing the GetSHA512 function in common!")
	var expectedOutput = []byte("a2273f492ea73fddc4f25c267b34b3b74998bd8a6301149e1e1c835678e3c0b90859fce22e4e7af33bde1711cbb924809aedf5d759d648d61774b7185c5dc02b")
	actualOutput := GetSHA512(originalString)
	actualStr := HexEncodeToString(actualOutput)
	if !bytes.Equal(expectedOutput, []byte(actualStr)) {
		t.Error(fmt.Sprintf("Test failed. Expected '%x'. Actual '%x'", expectedOutput, []byte(actualStr)))
	}
}

func TestGetSHA256(t *testing.T) {
	t.Parallel()
	var originalString = []byte("I am testing the GetSHA256 function in common!")
	var expectedOutput = []byte("0962813d7a9f739cdcb7f0c0be0c2a13bd630167e6e54468266e4af6b1ad9303")
	actualOutput := GetSHA256(originalString)
	actualStr := HexEncodeToString(actualOutput)
	if !bytes.Equal(expectedOutput, []byte(actualStr)) {
		t.Error(fmt.Sprintf("Test failed. Expected '%x'. Actual '%x'", expectedOutput, []byte(actualStr)))
	}
}

func TestStringToLower(t *testing.T) {
	t.Parallel()
	upperCaseString := "HEY MAN"
	expectedResult := "hey man"
	actualResult := StringToLower(upperCaseString)
	if actualResult != expectedResult {
		t.Error("...")
	}
}

func TestStringToUpper(t *testing.T) {
	t.Parallel()
	upperCaseString := "hey man"
	expectedResult := "HEY MAN"
	actualResult := StringToUpper(upperCaseString)
	if actualResult != expectedResult {
		t.Error("...")
	}
}

func TestHexEncodeToString(t *testing.T) {
	t.Parallel()
	originalInput := []byte("string")
	expectedOutput := "737472696e67"
	actualResult := HexEncodeToString(originalInput)
	if actualResult != expectedOutput {
		t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'", expectedOutput, actualResult))
	}
}

func TestBase64Decode(t *testing.T) {
	t.Parallel()
	originalInput := "aGVsbG8="
	expectedOutput := []byte("hello")
	actualResult, err := Base64Decode(originalInput)
	if !bytes.Equal(actualResult, expectedOutput) {
		t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'. Error: %s", expectedOutput, actualResult, err))
	}
}

func TestBase64Encode(t *testing.T) {
	t.Parallel()
	originalInput := []byte("hello")
	expectedOutput := "aGVsbG8="
	actualResult := Base64Encode(originalInput)
	if actualResult != expectedOutput {
		t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'", expectedOutput, actualResult))
	}
}

func TestStringSliceDifference(t *testing.T) {
	t.Parallel()
	originalInputOne := []string{"hello"}
	originalInputTwo := []string{"moto"}
	expectedOutput := []string{"hello moto"}
	actualResult := StringSliceDifference(originalInputOne, originalInputTwo)
	if reflect.DeepEqual(expectedOutput, actualResult) {
		t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'", expectedOutput, actualResult))
	}
}

func TestStringContains(t *testing.T) {
	t.Parallel()
	originalInput := "hello"
	originalInputSubstring := "he"
	expectedOutput := true
	actualResult := StringContains(originalInput, originalInputSubstring)
	if actualResult != expectedOutput {
		t.Error(fmt.Sprintf("Test failed. Expected '%t'. Actual '%t'", expectedOutput, actualResult))
	}
}

func TestDataContains(t *testing.T) {
	t.Parallel()
	originalHaystack := []string{"hello", "world", "data", "Contains", "string"}
	originalNeedle := "world"
	anotherNeedle := "thing"
	expectedOutput := true
	expectedOutputTwo := false
	actualResult := DataContains(originalHaystack, originalNeedle)
	if actualResult != expectedOutput {
		t.Error(fmt.Sprintf("Test failed. Expected '%t'. Actual '%t'", expectedOutput, actualResult))
	}
	actualResult = DataContains(originalHaystack, anotherNeedle)
	if actualResult != expectedOutputTwo {
		t.Error(fmt.Sprintf("Test failed. Expected '%t'. Actual '%t'", expectedOutputTwo, actualResult))
	}
}

func TestJoinStrings(t *testing.T) {
	t.Parallel()
	originalInputOne := []string{"hello", "moto"}
	seperator := ","
	expectedOutput := "hello,moto"
	actualResult := JoinStrings(originalInputOne, seperator)
	if expectedOutput != actualResult {
		t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'", expectedOutput, actualResult))
	}
}

func TestSplitStrings(t *testing.T) {
	t.Parallel()
	originalInputOne := "hello,moto"
	seperator := ","
	expectedOutput := []string{"hello", "moto"}
	actualResult := SplitStrings(originalInputOne, seperator)
	if !reflect.DeepEqual(expectedOutput, actualResult) {
		t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'", expectedOutput, actualResult))
	}
}

func TestRoundFloat(t *testing.T) {
	t.Parallel()
	originalInput := float64(1.4545445445)
	precisionInput := 2
	expectedOutput := float64(1.45)
	actualResult := RoundFloat(originalInput, precisionInput)
	if expectedOutput != actualResult {
		t.Error(fmt.Sprintf("Test failed. Expected '%f'. Actual '%f'.", expectedOutput, actualResult))
	}
}

func TestCalculateFee(t *testing.T) {
	t.Parallel()
	originalInput := float64(1)
	fee := float64(1)
	expectedOutput := float64(0.01)
	actualResult := CalculateFee(originalInput, fee)
	if expectedOutput != actualResult {
		t.Error(fmt.Sprintf("Test failed. Expected '%f'. Actual '%f'.", expectedOutput, actualResult))
	}
}

func TestCalculateAmountWithFee(t *testing.T) {
	t.Parallel()
	originalInput := float64(1)
	fee := float64(1)
	expectedOutput := float64(1.01)
	actualResult := CalculateAmountWithFee(originalInput, fee)
	if expectedOutput != actualResult {
		t.Error(fmt.Sprintf("Test failed. Expected '%f'. Actual '%f'.", expectedOutput, actualResult))
	}
}

func TestCalculatePercentageGainOrLoss(t *testing.T) {
	t.Parallel()
	originalInput := float64(9300)
	secondInput := float64(9000)
	expectedOutput := 3.3333333333333335
	actualResult := CalculatePercentageGainOrLoss(originalInput, secondInput)
	if expectedOutput != actualResult {
		t.Error(fmt.Sprintf("Test failed. Expected '%f'. Actual '%f'.", expectedOutput, actualResult))
	}
}

func TestCalculatePercentageDifference(t *testing.T) {
	t.Parallel()
	originalInput := float64(10)
	secondAmount := float64(5)
	expectedOutput := 66.66666666666666
	actualResult := CalculatePercentageDifference(originalInput, secondAmount)
	if expectedOutput != actualResult {
		t.Error(fmt.Sprintf("Test failed. Expected '%f'. Actual '%f'.", expectedOutput, actualResult))
	}
}

func TestCalculateNetProfit(t *testing.T) {
	t.Parallel()
	amount := float64(5)
	priceThen := float64(1)
	priceNow := float64(10)
	costs := float64(1)
	expectedOutput := float64(44)
	actualResult := CalculateNetProfit(amount, priceThen, priceNow, costs)
	if expectedOutput != actualResult {
		t.Error(fmt.Sprintf("Test failed. Expected '%f'. Actual '%f'.", expectedOutput, actualResult))
	}
}

func TestExtractHost(t *testing.T) {
	t.Parallel()
	address := "localhost:1337"
	expectedOutput := "localhost"
	actualResult := ExtractHost(address)
	if expectedOutput != actualResult {
		t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'.", expectedOutput, actualResult))
	}

	address = "192.168.1.100:1337"
	expectedOutput = "192.168.1.100"
	actualResult = ExtractHost(address)
	if expectedOutput != actualResult {
		t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'.", expectedOutput, actualResult))
	}
}

func TestExtractPort(t *testing.T) {
	t.Parallel()
	address := "localhost:1337"
	expectedOutput := 1337
	actualResult := ExtractPort(address)
	if expectedOutput != actualResult {
		t.Error(fmt.Sprintf("Test failed. Expected '%d'. Actual '%d'.", expectedOutput, actualResult))
	}
}

func TestUnixTimestampToTime(t *testing.T) {
	t.Parallel()
	testTime := int64(1489439831)
	tm := time.Unix(testTime, 0)
	expectedOutput := "2017-03-13 21:17:11 +0000 UTC"
	actualResult := UnixTimestampToTime(testTime)
	if tm.String() != actualResult.String() {
		t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'.", expectedOutput, actualResult))
	}
}

func TestUnixTimestampStrToTime(t *testing.T) {
	t.Parallel()
	testTime := "1489439831"
	expectedOutput := "2017-03-13 21:17:11 +0000 UTC"
	actualResult, err := UnixTimestampStrToTime(testTime)
	if err != nil {
		t.Error(err)
	}

	if actualResult.UTC().String() != expectedOutput {
		t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'.", expectedOutput, actualResult))
	}
}

func TestGetURIPath(t *testing.T) {
	t.Parallel()
	// mapping of input vs expected result
	testTable := map[string]string{
		"https://api.gdax.com/accounts":         "/accounts",
		"https://api.gdax.com/accounts?a=1&b=2": "/accounts?a=1&b=2",
		"ht:tp:/invalidurl":                     "",
	}
	for testInput, expectedOutput := range testTable {
		actualOutput := GetURIPath(testInput)
		if actualOutput != expectedOutput {
			t.Error(fmt.Sprintf("Test failed. Expected '%s'. Actual '%s'.",
				expectedOutput, actualOutput))
		}
	}
}
<|endoftext|>