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

package main

import (
	"os"
	"log"
	"fmt"
	"time"
	"strings"

//	"bytes"
    "encoding/binary"
    "math"
    "strconv"
//    "unsafe"

	"github.com/nats-io/go-nats"
	"github.com/gocql/gocql"
)

const query = "INSERT INTO raw_data (" +
			  "line, transformer, usagePoint, year, month, day, hour, minute, day_of_week, voltage, demand, " +
			  "val3, val4, val5, val6, val7, val8, val9, val10, val11, val12) " +
			  "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"

// http://docs.datastax.com/en/cql/3.3/cql/cql_using/useCounters.html
const increment = "UPDATE raw_data_count SET count = count + 1 WHERE slot = ?"

var Session *gocql.Session
var Cluster *gocql.ClusterConfig

func main() {
	log.Print("Welcome to the NATS to Cassandra Bridge")

	log.Print("The time is", time.Now())

	nats_uri := os.Getenv("NATS_URI")
	nc, _ := nats.Connect(nats_uri)

	log.Print("Subscribed to NATS: ", nats_uri)

	nats_subject := os.Getenv("NATS_SUBJECT")
	log.Print("NATS Subject: ", nats_subject)

	// CASSANDRA
	cassandra_url := os.Getenv("CASSANDRA_URL")
	log.Print("Cassandra URL: ", cassandra_url)

	// LOG LEVEL
	log_level := os.Getenv("LOG_LEVEL")
	log.Print("LOG LEVEL: ", log_level)

  // TASK_SLOT
  task_slot := os.Getenv("TASK_SLOT")
  log.Print("TASK SLOT: ", task_slot)

	// connect to the Cluster
	Cluster = gocql.NewCluster(cassandra_url)
	Cluster.Keyspace = "smartmeter"

	cluster_consistency := os.Getenv("CASSANDRA_INJECT_CONSISTENCY")
	Cluster.Consistency = gocql.ParseConsistency(cluster_consistency)
	log.Print("CONSISTENCY: ", Cluster.Consistency)

  cluster_timeout := os.Getenv("CASSANDRA_TIMEOUT")
  if (cluster_timeout != "") {
    timeout, err := strconv.Atoi(cluster_timeout)
    if (err == nil) {
      Cluster.Timeout = time.Duration(timeout) * time.Millisecond
      log.Print("(Provided) CASSANDRA_TIMEOUT: ", Cluster.Timeout)
    } else {
      log.Panicf("Unable to parse CASSANDRA_TIMEOUT (%s) into int64", cluster_timeout)
    }
  } else {
    log.Print("(Default) CASSANDRA_TIMEOUT: ", Cluster.Timeout)
  }

	createSession()
  defer Session.Close()

	log.Print("Connected to Cassandra")

	// Simple Async Subscriber
	nc.QueueSubscribe(nats_subject, "cassandra_inject", func(m *nats.Msg) {
		go insertIntoCassandra(m, task_slot, log_level)
	})

	log.Print("Ready to store NATS messages into CASSANDRA")

  // To keep the App alive
	for {
		time.Sleep(30 * time.Second)
		// log.Print(time.Now())
	}

}

func createSession() {
  log.Print("Cluster.CreateSession()")
  var err error
  Session, err = Cluster.CreateSession()
  if (err != nil) {
		log.Fatalf("Could not connect to Cassandra Cluster %s", err)
	}
}

func insertIntoCassandra(m *nats.Msg, task_slot string, log_level string) {
	/*** Point ***/

	// https://www.dotnetperls.com/split-go
	subjects := strings.Split(m.Subject, ".")
	// smartmeter.voltage.data.3.3.2	 | (2016-11-16T20:05:04,116.366646)
	len := len(subjects)

	// http://stackoverflow.com/questions/30299649/golang-converting-string-to-specific-type-of-int-int8-int16-int32-int64
	line, _ := strconv.ParseInt(subjects[len -3], 10, 8) // tinyint,    // 8-bit signed int
	transformer, _ := strconv.ParseInt(subjects[len -2], 10, 32)	//  int, // 32-bit signed int
	usagePoint, _ := strconv.ParseInt(subjects[len -1], 10, 32)	//  int,

	/*** Date ***/

	longBytes := m.Data[:8]
	// http://stackoverflow.com/questions/22491876/convert-byte-array-uint8-to-float64-in-golang
	epoch := int64(binary.BigEndian.Uint64(longBytes))
	date := time.Unix(epoch, 0)

	// https://golang.org/pkg/time/#Time
	year, month, day := date.Date()
	hour, minute, _ := date.Clock()
	day_of_week := date.Weekday()

	/*** Voltage ***/

	voltageFloatBytes := m.Data[8:12]
	// http://stackoverflow.com/questions/22491876/convert-byte-array-uint8-to-float64-in-golang
	voltage := math.Float32frombits(binary.BigEndian.Uint32(voltageFloatBytes))

	/*** Demand ***/

	demandFloatBytes := m.Data[12:16]
	// http://stackoverflow.com/questions/22491876/convert-byte-array-uint8-to-float64-in-golang
	demand := math.Float32frombits(binary.BigEndian.Uint32(demandFloatBytes))

	/*** Remaining Values ***/

	var values [10]float32
	for i := 0; i < 10; i++ {
		values[i] = math.Float32frombits(binary.BigEndian.Uint32(m.Data[(16+4*i):(20+4*i)]))
	}

  // fmt.Print(".")

	if (log_level == "TRACE") {
		s := fmt.Sprintf("- v: %d", voltage)
		log.Print(s)
	}

  if (Session == nil || Session.Closed()) {
    createSession()
  }

  /** insert the Data into Cassandra **/

  if err := Session.Query(query,
      int8(line), int32(transformer), int32(usagePoint), int16(year), int8(month), int8(day),
      int8(hour), int8(minute), int8(day_of_week), voltage, demand,
      values[0], values[1], values[2], values[3], values[4],
      values[5], values[6], values[7], values[8], values[9]).Exec(); err != nil {
      log.Panic(err)
  } else {
    if err := Session.Query(increment, task_slot).Exec(); err != nil {
        log.Panic(err)
    }
  }
}
<commit_msg>[ci skip] Tabulation fixed<commit_after>// MIT License
//
// Copyright (c) 2016-2017 Logimethods
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

package main

import (
  "os"
  "log"
  "fmt"
  "time"
  "strings"

//  "bytes"
    "encoding/binary"
    "math"
    "strconv"
//    "unsafe"

  "github.com/nats-io/go-nats"
  "github.com/gocql/gocql"
)

const query = "INSERT INTO raw_data (" +
        "line, transformer, usagePoint, year, month, day, hour, minute, day_of_week, voltage, demand, " +
        "val3, val4, val5, val6, val7, val8, val9, val10, val11, val12) " +
        "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"

// http://docs.datastax.com/en/cql/3.3/cql/cql_using/useCounters.html
const increment = "UPDATE raw_data_count SET count = count + 1 WHERE slot = ?"

var Session *gocql.Session
var Cluster *gocql.ClusterConfig

func main() {
  log.Print("Welcome to the NATS to Cassandra Bridge")

  log.Print("The time is", time.Now())

  nats_uri := os.Getenv("NATS_URI")
  nc, _ := nats.Connect(nats_uri)

  log.Print("Subscribed to NATS: ", nats_uri)

  nats_subject := os.Getenv("NATS_SUBJECT")
  log.Print("NATS Subject: ", nats_subject)

  // CASSANDRA
  cassandra_url := os.Getenv("CASSANDRA_URL")
  log.Print("Cassandra URL: ", cassandra_url)

  // LOG LEVEL
  log_level := os.Getenv("LOG_LEVEL")
  log.Print("LOG LEVEL: ", log_level)

  // TASK_SLOT
  task_slot := os.Getenv("TASK_SLOT")
  log.Print("TASK SLOT: ", task_slot)

  // connect to the Cluster
  Cluster = gocql.NewCluster(cassandra_url)
  Cluster.Keyspace = "smartmeter"

  cluster_consistency := os.Getenv("CASSANDRA_INJECT_CONSISTENCY")
  Cluster.Consistency = gocql.ParseConsistency(cluster_consistency)
  log.Print("CONSISTENCY: ", Cluster.Consistency)

  cluster_timeout := os.Getenv("CASSANDRA_TIMEOUT")
  if (cluster_timeout != "") {
    timeout, err := strconv.Atoi(cluster_timeout)
    if (err == nil) {
      Cluster.Timeout = time.Duration(timeout) * time.Millisecond
      log.Print("(Provided) CASSANDRA_TIMEOUT: ", Cluster.Timeout)
    } else {
      log.Panicf("Unable to parse CASSANDRA_TIMEOUT (%s) into int64", cluster_timeout)
    }
  } else {
    log.Print("(Default) CASSANDRA_TIMEOUT: ", Cluster.Timeout)
  }

  createSession()
  defer Session.Close()

  log.Print("Connected to Cassandra")

  // Simple Async Subscriber
  nc.QueueSubscribe(nats_subject, "cassandra_inject", func(m *nats.Msg) {
    go insertIntoCassandra(m, task_slot, log_level)
  })

  log.Print("Ready to store NATS messages into CASSANDRA")

  // To keep the App alive
  for {
    time.Sleep(30 * time.Second)
    // log.Print(time.Now())
  }

}

func createSession() {
  log.Print("Cluster.CreateSession()")
  var err error
  Session, err = Cluster.CreateSession()
  if (err != nil) {
    log.Fatalf("Could not connect to Cassandra Cluster %s", err)
  }
}

func insertIntoCassandra(m *nats.Msg, task_slot string, log_level string) {
  /*** Point ***/

  // https://www.dotnetperls.com/split-go
  subjects := strings.Split(m.Subject, ".")
  // smartmeter.voltage.data.3.3.2   | (2016-11-16T20:05:04,116.366646)
  len := len(subjects)

  // http://stackoverflow.com/questions/30299649/golang-converting-string-to-specific-type-of-int-int8-int16-int32-int64
  line, _ := strconv.ParseInt(subjects[len -3], 10, 8) // tinyint,    // 8-bit signed int
  transformer, _ := strconv.ParseInt(subjects[len -2], 10, 32)  //  int, // 32-bit signed int
  usagePoint, _ := strconv.ParseInt(subjects[len -1], 10, 32)  //  int,

  /*** Date ***/

  longBytes := m.Data[:8]
  // http://stackoverflow.com/questions/22491876/convert-byte-array-uint8-to-float64-in-golang
  epoch := int64(binary.BigEndian.Uint64(longBytes))
  date := time.Unix(epoch, 0)

  // https://golang.org/pkg/time/#Time
  year, month, day := date.Date()
  hour, minute, _ := date.Clock()
  day_of_week := date.Weekday()

  /*** Voltage ***/

  voltageFloatBytes := m.Data[8:12]
  // http://stackoverflow.com/questions/22491876/convert-byte-array-uint8-to-float64-in-golang
  voltage := math.Float32frombits(binary.BigEndian.Uint32(voltageFloatBytes))

  /*** Demand ***/

  demandFloatBytes := m.Data[12:16]
  // http://stackoverflow.com/questions/22491876/convert-byte-array-uint8-to-float64-in-golang
  demand := math.Float32frombits(binary.BigEndian.Uint32(demandFloatBytes))

  /*** Remaining Values ***/

  var values [10]float32
  for i := 0; i < 10; i++ {
    values[i] = math.Float32frombits(binary.BigEndian.Uint32(m.Data[(16+4*i):(20+4*i)]))
  }

  // fmt.Print(".")

  if (log_level == "TRACE") {
    s := fmt.Sprintf("- v: %d", voltage)
    log.Print(s)
  }

  if (Session == nil || Session.Closed()) {
    createSession()
  }

  /** insert the Data into Cassandra **/

  if err := Session.Query(query,
      int8(line), int32(transformer), int32(usagePoint), int16(year), int8(month), int8(day),
      int8(hour), int8(minute), int8(day_of_week), voltage, demand,
      values[0], values[1], values[2], values[3], values[4],
      values[5], values[6], values[7], values[8], values[9]).Exec(); err != nil {
      log.Panic(err)
  } else {
    if err := Session.Query(increment, task_slot).Exec(); err != nil {
        log.Panic(err)
    }
  }
}
<|endoftext|>


<commit_before>package youtubeapi

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
	"time"
)

// Youtube ...
type Youtube struct {
	Key string
}

// New Returns a Youtube struct with the supplied key
//		key: Google API key
func New(key string) *Youtube {
	return &Youtube{
		Key: key,
	}
}

// SearchResult is the json data retrieved from the youtube api search endpoint.
type SearchResult struct {
	Kind          string `json:"kind"`
	Etag          string `json:"etag"`
	NextPageToken string `json:"nextPageToken"`
	RegionCode    string `json:"regionCode"`
	PageInfo      struct {
		TotalResults   int `json:"totalResults"`
		ResultsPerPage int `json:"resultsPerPage"`
	} `json:"pageInfo"`
	Items []Item `json:"items"`
}

// Item stores information about videos
type Item struct {
	Kind string `json:"kind"`
	Etag string `json:"etag"`
	ID   struct {
		Kind    string `json:"kind"`
		VideoID string `json:"videoId"`
	} `json:"id"`
	Snippet struct {
		PublishedAt time.Time `json:"publishedAt"`
		ChannelID   string    `json:"channelId"`
		Title       string    `json:"title"`
		Description string    `json:"description"`
		Thumbnails  struct {
			Default struct {
				URL    string `json:"url"`
				Width  int    `json:"width"`
				Height int    `json:"height"`
			} `json:"default"`
			Medium struct {
				URL    string `json:"url"`
				Width  int    `json:"width"`
				Height int    `json:"height"`
			} `json:"medium"`
			High struct {
				URL    string `json:"url"`
				Width  int    `json:"width"`
				Height int    `json:"height"`
			} `json:"high"`
		} `json:"thumbnails"`
		ChannelTitle         string `json:"channelTitle"`
		LiveBroadcastContent string `json:"liveBroadcastContent"`
	} `json:"snippet"`
}

// Search searches youtube for videos with the supplied query.
//		query: The query to search for.
func (y *Youtube) Search(query string, maxResults int) (*SearchResult, error) {
	resp, err := http.Get(fmt.Sprintf("https://www.googleapis.com/youtube/v3/search?part=snippet&q=%s&key=%s&maxResults=%d", url.QueryEscape(query), y.Key, maxResults))
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode != 200 {
		return nil, errors.New(http.StatusText(resp.StatusCode))
	}

	var searchRes SearchResult
	err = json.NewDecoder(resp.Body).Decode(&searchRes)
	if err != nil {
		return nil, err
	}

	return &searchRes, nil
}

// ScrapeSearch search youtube without an api key
//		query: The query to search for.
	resp, err := http.Get("https://www.youtube.com/results?search_query=" + url.QueryEscape(query))
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var body string
	if b, err := ioutil.ReadAll(resp.Body); err == nil {
		body = string(b)
	} else {
		return nil, err
	}

	videostart := `<a href="/watch?v=`
	videoEnd := `"`

	urls := []string{}
		startIndex := strings.Index(body, videostart)
		if startIndex < 0 {
			break
		}
		startIndex += 9

		endIndex := strings.Index(body[startIndex:], videoEnd)
		if endIndex < 0 {
			break
		}
		endIndex += startIndex

		urls = append(urls, "https://www.youtube.com"+body[startIndex:endIndex])
		body = body[endIndex:]
	}

	return urls, nil
}
<commit_msg>Youtube search scraper to retrieve videos without an api key<commit_after>package youtubeapi

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
	"time"
)

// Youtube ...
type Youtube struct {
	Key string
}

// New Returns a Youtube struct with the supplied key
//		key: Google API key
func New(key string) *Youtube {
	return &Youtube{
		Key: key,
	}
}

// SearchResult is the json data retrieved from the youtube api search endpoint.
type SearchResult struct {
	Kind          string `json:"kind"`
	Etag          string `json:"etag"`
	NextPageToken string `json:"nextPageToken"`
	RegionCode    string `json:"regionCode"`
	PageInfo      struct {
		TotalResults   int `json:"totalResults"`
		ResultsPerPage int `json:"resultsPerPage"`
	} `json:"pageInfo"`
	Items []Item `json:"items"`
}

// Item stores information about videos
type Item struct {
	Kind string `json:"kind"`
	Etag string `json:"etag"`
	ID   struct {
		Kind    string `json:"kind"`
		VideoID string `json:"videoId"`
	} `json:"id"`
	Snippet struct {
		PublishedAt time.Time `json:"publishedAt"`
		ChannelID   string    `json:"channelId"`
		Title       string    `json:"title"`
		Description string    `json:"description"`
		Thumbnails  struct {
			Default struct {
				URL    string `json:"url"`
				Width  int    `json:"width"`
				Height int    `json:"height"`
			} `json:"default"`
			Medium struct {
				URL    string `json:"url"`
				Width  int    `json:"width"`
				Height int    `json:"height"`
			} `json:"medium"`
			High struct {
				URL    string `json:"url"`
				Width  int    `json:"width"`
				Height int    `json:"height"`
			} `json:"high"`
		} `json:"thumbnails"`
		ChannelTitle         string `json:"channelTitle"`
		LiveBroadcastContent string `json:"liveBroadcastContent"`
	} `json:"snippet"`
}

// Search searches youtube for videos with the supplied query.
//		query: The query to search for.
func (y *Youtube) Search(query string, maxResults int) (*SearchResult, error) {
	resp, err := http.Get(fmt.Sprintf("https://www.googleapis.com/youtube/v3/search?part=snippet&q=%s&key=%s&maxResults=%d", url.QueryEscape(query), y.Key, maxResults))
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode != 200 {
		return nil, errors.New(http.StatusText(resp.StatusCode))
	}

	var searchRes SearchResult
	err = json.NewDecoder(resp.Body).Decode(&searchRes)
	if err != nil {
		return nil, err
	}

	return &searchRes, nil
}

// ScrapeSearch search youtube without an api key
//		query: The query to search for.
func ScrapeSearch(query string, limit int) ([]string, error) {
	resp, err := http.Get("https://www.youtube.com/results?search_query=" + url.QueryEscape(query))
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var body string
	if b, err := ioutil.ReadAll(resp.Body); err == nil {
		body = string(b)
	} else {
		return nil, err
	}

	videostart := `<a href="/watch?v=`
	videoEnd := `"`

	urls := []string{}
	for i := 0; i < limit; i++ {
		startIndex := strings.Index(body, videostart)
		if startIndex < 0 {
			break
		}
		startIndex += 9

		endIndex := strings.Index(body[startIndex:], videoEnd)
		if endIndex < 0 {
			break
		}
		endIndex += startIndex

		urls = append(urls, "https://www.youtube.com"+body[startIndex:endIndex])
		body = body[endIndex:]
	}

	return urls, nil
}
<|endoftext|>


<commit_before>package bham

import (
	"fmt"
	"reflect"
	"regexp"
	"runtime"
	"strings"
	"testing"
)

type Test struct {
	T       *testing.T
	F       *FTest
	section string
}
type FTest struct {
	T       *testing.T
	section string
}

func (test *Test) Section(s string) {
	test.section = s
	test.F.section = s
}
func (test *Test) logDetails() {
	_, fn, l, _ := runtime.Caller(2)
	if test.section != "" {
		test.T.Logf("Error in %s Section in file %s on Line %v",
			test.section,
			fn,
			l,
		)
	} else {
		test.T.Logf("Error in File %s on Line %v", fn, l)
	}
}
func (test *FTest) logDetails() {
	_, fn, l, _ := runtime.Caller(2)
	if test.section != "" {
		test.T.Logf("Error in %s Section in File %s on Line %v",
			test.section,
			fn,
			l,
		)
	} else {
		test.T.Logf("Error in File %s on Line %v", fn, l)
	}
}

// Nil tests
func (test *Test) IsNil(v interface{}, msgs ...interface{}) {
	if !testIsNil(v) {
		test.logDetails()
		test.T.Log("Nil check failed")
		if len(msgs) > 0 {
			test.T.Error(msgs...)
		} else {
			test.T.Error(v, "is not nil")
		}
	}
}
func (test *Test) IsNotNil(v interface{}, msgs ...interface{}) {
	if testIsNil(v) {
		test.logDetails()
		test.T.Error(msgs...)
	}
}

// bool tests
func (test *Test) IsTrue(b bool, msgs ...interface{}) {
	if !b {
		test.logDetails()
		test.T.Error(msgs...)
	}
}
func (test *Test) IsFalse(b bool, msgs ...interface{}) {
	if b {
		test.logDetails()
		test.T.Error(msgs...)
	}
}

// Equality test
func (test *Test) AreEqual(x, y interface{}, msgs ...interface{}) {
	if !(reflect.DeepEqual(x, y) || strEqual(x, y)) {
		test.logDetails()
		test.T.Log("Equality check failed")
		if len(msgs) > 0 {
			test.T.Error(msgs...)
		} else {
			test.T.Error(x, "!=", y)
		}
	}
}
func (test *Test) AreNotEqual(x, y interface{}, msgs ...interface{}) {
	if reflect.DeepEqual(x, y) || strEqual(x, y) {
		test.logDetails()
		test.T.Log("Inequality check failed")
		test.T.Error(msgs...)
	}
}

// String tests
func (test *Test) StartsWith(s, pre string, msgs ...interface{}) {
	if !strings.HasPrefix(s, pre) {
		test.logDetails()
		test.T.Error(msgs...)
	}
}
func (test *Test) EndsWith(s, post string, msgs ...interface{}) {
	if !strings.HasSuffix(s, post) {
		test.logDetails()
		test.T.Error(msgs...)
	}
}
func (test *Test) Matches(s, regex string, msgs ...interface{}) {
	matches, err := regexp.MatchString(regex, s)
	if err != nil {
		test.logDetails()
		panic(err)
	} else if !matches {
		test.logDetails()
		test.T.Error(msgs...)
	}
}
func (test *Test) NotMatches(s, regex string, msgs ...interface{}) {
	matches, err := regexp.MatchString(regex, s)
	if err != nil {
		test.logDetails()
		panic(err)
	} else if matches {
		test.logDetails()
		test.T.Error(msgs...)
	}
}

// Nil Format tests
func (test *FTest) IsNil(v interface{}, msgFormat string, msgs ...interface{}) {
	if !testIsNil(v) {
		test.logDetails()
		test.T.Errorf(msgFormat, msgs...)
	}
}
func (test *FTest) IsNotNil(v interface{}, msgFormat string, msgs ...interface{}) {
	if testIsNil(v) {
		test.logDetails()
		test.T.Errorf(msgFormat, msgs...)
	}
}

// bool tests
func (test *FTest) IsTrue(b bool, msgFormat string, msgs ...interface{}) {
	if !b {
		test.logDetails()
		test.T.Errorf(msgFormat, msgs...)
	}
}
func (test *FTest) IsFalse(b bool, msgFormat string, msgs ...interface{}) {
	if b {
		test.logDetails()
		test.T.Errorf(msgFormat, msgs...)
	}
}

// Equality test
func (test *FTest) AreEqual(x, y interface{}, msgFormat string, msgs ...interface{}) {
	if !(reflect.DeepEqual(x, y) || strEqual(x, y)) {
		test.logDetails()
		test.T.Errorf(msgFormat, msgs...)
	}
}
func (test *FTest) AreNotEqual(x, y interface{}, msgFormat string, msgs ...interface{}) {
	if reflect.DeepEqual(x, y) || strEqual(x, y) {
		test.logDetails()
		test.T.Errorf(msgFormat, msgs...)
	}
}

// String tests
func (test *FTest) StartsWith(s, pre, msgFormat string, msgs ...interface{}) {
	if !strings.HasPrefix(s, pre) {
		test.logDetails()
		test.T.Errorf(msgFormat, msgs...)
	}
}
func (test *FTest) EndsWith(s, post, msgFormat string, msgs ...interface{}) {
	if !strings.HasSuffix(s, post) {
		test.T.Errorf(msgFormat, msgs...)
	}
}
func (test *FTest) Matches(s, regex, msgFormat string, msgs ...interface{}) {
	matches, err := regexp.MatchString(regex, s)
	if err != nil {
		test.logDetails()
		panic(err)
	} else if !matches {
		test.logDetails()
		test.T.Errorf(msgFormat, msgs...)
	}
}
func (test *FTest) NotMatches(s, regex, msgFormat string, msgs ...interface{}) {
	matches, err := regexp.MatchString(regex, s)
	if err != nil {
		test.logDetails()
		panic(err)
	} else if matches {
		test.logDetails()
		test.T.Errorf(msgFormat, msgs...)
	}
}

func testIsNil(v interface{}) bool {
	return v == nil || reflect.ValueOf(v).IsNil()
}

func strEqual(x, y interface{}) bool {
	return fmt.Sprint(x) == fmt.Sprint(y)
}

func Within(t *testing.T, f func(*Test)) {
	f(&Test{T: t, F: &FTest{T: t}})
}
<commit_msg>fix package name<commit_after>package assert

import (
	"fmt"
	"reflect"
	"regexp"
	"runtime"
	"strings"
	"testing"
)

type Test struct {
	T       *testing.T
	F       *FTest
	section string
}
type FTest struct {
	T       *testing.T
	section string
}

func (test *Test) Section(s string) {
	test.section = s
	test.F.section = s
}
func (test *Test) logDetails() {
	_, fn, l, _ := runtime.Caller(2)
	if test.section != "" {
		test.T.Logf("Error in %s Section in file %s on Line %v",
			test.section,
			fn,
			l,
		)
	} else {
		test.T.Logf("Error in File %s on Line %v", fn, l)
	}
}
func (test *FTest) logDetails() {
	_, fn, l, _ := runtime.Caller(2)
	if test.section != "" {
		test.T.Logf("Error in %s Section in File %s on Line %v",
			test.section,
			fn,
			l,
		)
	} else {
		test.T.Logf("Error in File %s on Line %v", fn, l)
	}
}

// Nil tests
func (test *Test) IsNil(v interface{}, msgs ...interface{}) {
	if !testIsNil(v) {
		test.logDetails()
		test.T.Log("Nil check failed")
		if len(msgs) > 0 {
			test.T.Error(msgs...)
		} else {
			test.T.Error(v, "is not nil")
		}
	}
}
func (test *Test) IsNotNil(v interface{}, msgs ...interface{}) {
	if testIsNil(v) {
		test.logDetails()
		test.T.Error(msgs...)
	}
}

// bool tests
func (test *Test) IsTrue(b bool, msgs ...interface{}) {
	if !b {
		test.logDetails()
		test.T.Error(msgs...)
	}
}
func (test *Test) IsFalse(b bool, msgs ...interface{}) {
	if b {
		test.logDetails()
		test.T.Error(msgs...)
	}
}

// Equality test
func (test *Test) AreEqual(x, y interface{}, msgs ...interface{}) {
	if !(reflect.DeepEqual(x, y) || strEqual(x, y)) {
		test.logDetails()
		test.T.Log("Equality check failed")
		if len(msgs) > 0 {
			test.T.Error(msgs...)
		} else {
			test.T.Error(x, "!=", y)
		}
	}
}
func (test *Test) AreNotEqual(x, y interface{}, msgs ...interface{}) {
	if reflect.DeepEqual(x, y) || strEqual(x, y) {
		test.logDetails()
		test.T.Log("Inequality check failed")
		test.T.Error(msgs...)
	}
}

// String tests
func (test *Test) StartsWith(s, pre string, msgs ...interface{}) {
	if !strings.HasPrefix(s, pre) {
		test.logDetails()
		test.T.Error(msgs...)
	}
}
func (test *Test) EndsWith(s, post string, msgs ...interface{}) {
	if !strings.HasSuffix(s, post) {
		test.logDetails()
		test.T.Error(msgs...)
	}
}
func (test *Test) Matches(s, regex string, msgs ...interface{}) {
	matches, err := regexp.MatchString(regex, s)
	if err != nil {
		test.logDetails()
		panic(err)
	} else if !matches {
		test.logDetails()
		test.T.Error(msgs...)
	}
}
func (test *Test) NotMatches(s, regex string, msgs ...interface{}) {
	matches, err := regexp.MatchString(regex, s)
	if err != nil {
		test.logDetails()
		panic(err)
	} else if matches {
		test.logDetails()
		test.T.Error(msgs...)
	}
}

// Nil Format tests
func (test *FTest) IsNil(v interface{}, msgFormat string, msgs ...interface{}) {
	if !testIsNil(v) {
		test.logDetails()
		test.T.Errorf(msgFormat, msgs...)
	}
}
func (test *FTest) IsNotNil(v interface{}, msgFormat string, msgs ...interface{}) {
	if testIsNil(v) {
		test.logDetails()
		test.T.Errorf(msgFormat, msgs...)
	}
}

// bool tests
func (test *FTest) IsTrue(b bool, msgFormat string, msgs ...interface{}) {
	if !b {
		test.logDetails()
		test.T.Errorf(msgFormat, msgs...)
	}
}
func (test *FTest) IsFalse(b bool, msgFormat string, msgs ...interface{}) {
	if b {
		test.logDetails()
		test.T.Errorf(msgFormat, msgs...)
	}
}

// Equality test
func (test *FTest) AreEqual(x, y interface{}, msgFormat string, msgs ...interface{}) {
	if !(reflect.DeepEqual(x, y) || strEqual(x, y)) {
		test.logDetails()
		test.T.Errorf(msgFormat, msgs...)
	}
}
func (test *FTest) AreNotEqual(x, y interface{}, msgFormat string, msgs ...interface{}) {
	if reflect.DeepEqual(x, y) || strEqual(x, y) {
		test.logDetails()
		test.T.Errorf(msgFormat, msgs...)
	}
}

// String tests
func (test *FTest) StartsWith(s, pre, msgFormat string, msgs ...interface{}) {
	if !strings.HasPrefix(s, pre) {
		test.logDetails()
		test.T.Errorf(msgFormat, msgs...)
	}
}
func (test *FTest) EndsWith(s, post, msgFormat string, msgs ...interface{}) {
	if !strings.HasSuffix(s, post) {
		test.T.Errorf(msgFormat, msgs...)
	}
}
func (test *FTest) Matches(s, regex, msgFormat string, msgs ...interface{}) {
	matches, err := regexp.MatchString(regex, s)
	if err != nil {
		test.logDetails()
		panic(err)
	} else if !matches {
		test.logDetails()
		test.T.Errorf(msgFormat, msgs...)
	}
}
func (test *FTest) NotMatches(s, regex, msgFormat string, msgs ...interface{}) {
	matches, err := regexp.MatchString(regex, s)
	if err != nil {
		test.logDetails()
		panic(err)
	} else if matches {
		test.logDetails()
		test.T.Errorf(msgFormat, msgs...)
	}
}

func testIsNil(v interface{}) bool {
	return v == nil || reflect.ValueOf(v).IsNil()
}

func strEqual(x, y interface{}) bool {
	return fmt.Sprint(x) == fmt.Sprint(y)
}

func Within(t *testing.T, f func(*Test)) {
	f(&Test{T: t, F: &FTest{T: t}})
}
<|endoftext|>


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

package swag

import (
	"math"
	"strconv"
	"testing"

	"github.com/stretchr/testify/assert"
)

// These are really dumb tests

func TestConvertBool(t *testing.T) {
	for k := range evaluatesAsTrue {
		r, err := ConvertBool(k)
		if assert.NoError(t, err) {
			assert.True(t, r)
		}
	}
	for _, k := range []string{"a", "", "0", "false", "unchecked"} {
		r, err := ConvertBool(k)
		if assert.NoError(t, err) {
			assert.False(t, r)
		}
	}
}

func TestConvertFloat32(t *testing.T) {
	validFloats := []float32{1.0, -1, math.MaxFloat32, math.SmallestNonzeroFloat32, 0, 5.494430303}
	invalidFloats := []string{"a", strconv.FormatFloat(math.MaxFloat64, 'f', -1, 64), "true"}

	for _, f := range validFloats {
		c, err := ConvertFloat32(FormatFloat32(f))
		if assert.NoError(t, err) {
			assert.EqualValues(t, f, c)
		}
	}
	for _, f := range invalidFloats {
		_, err := ConvertFloat32(f)
		assert.Error(t, err, "expected '"+f+"' to generate an error")
	}
}

func TestConvertFloat64(t *testing.T) {
	validFloats := []float64{1.0, -1, float64(math.MaxFloat32), float64(math.SmallestNonzeroFloat32), math.MaxFloat64, math.SmallestNonzeroFloat64, 0, 5.494430303}
	invalidFloats := []string{"a", "true"}

	for _, f := range validFloats {
		c, err := ConvertFloat64(FormatFloat64(f))
		if assert.NoError(t, err) {
			assert.EqualValues(t, f, c)
		}
	}
	for _, f := range invalidFloats {
		_, err := ConvertFloat64(f)
		assert.Error(t, err, "expected '"+f+"' to generate an error")
	}
}

func TestConvertInt8(t *testing.T) {
	validInts := []int8{0, 1, -1, math.MaxInt8, math.MinInt8}
	invalidInts := []string{"1.233", "a", "false", strconv.FormatInt(int64(math.MaxInt64), 10)}

	for _, f := range validInts {
		c, err := ConvertInt8(FormatInt8(f))
		if assert.NoError(t, err) {
			assert.EqualValues(t, f, c)
		}
	}
	for _, f := range invalidInts {
		_, err := ConvertInt8(f)
		assert.Error(t, err, "expected '"+f+"' to generate an error")
	}
}

func TestConvertInt16(t *testing.T) {
	validInts := []int16{0, 1, -1, math.MaxInt8, math.MinInt8, math.MaxInt16, math.MinInt16}
	invalidInts := []string{"1.233", "a", "false", strconv.FormatInt(int64(math.MaxInt64), 10)}

	for _, f := range validInts {
		c, err := ConvertInt16(FormatInt16(f))
		if assert.NoError(t, err) {
			assert.EqualValues(t, f, c)
		}
	}
	for _, f := range invalidInts {
		_, err := ConvertInt16(f)
		assert.Error(t, err, "expected '"+f+"' to generate an error")
	}
}

func TestConvertInt32(t *testing.T) {
	validInts := []int32{0, 1, -1, math.MaxInt8, math.MinInt8, math.MaxInt16, math.MinInt16, math.MinInt32, math.MaxInt32}
	invalidInts := []string{"1.233", "a", "false", strconv.FormatInt(int64(math.MaxInt64), 10)}

	for _, f := range validInts {
		c, err := ConvertInt32(FormatInt32(f))
		if assert.NoError(t, err) {
			assert.EqualValues(t, f, c)
		}
	}
	for _, f := range invalidInts {
		_, err := ConvertInt32(f)
		assert.Error(t, err, "expected '"+f+"' to generate an error")
	}
}

func TestConvertInt64(t *testing.T) {
	validInts := []int64{0, 1, -1, math.MaxInt8, math.MinInt8, math.MaxInt16, math.MinInt16, math.MinInt32, math.MaxInt32, math.MaxInt64, math.MinInt64}
	invalidInts := []string{"1.233", "a", "false"}

	for _, f := range validInts {
		c, err := ConvertInt64(FormatInt64(f))
		if assert.NoError(t, err) {
			assert.EqualValues(t, f, c)
		}
	}
	for _, f := range invalidInts {
		_, err := ConvertInt64(f)
		assert.Error(t, err, "expected '"+f+"' to generate an error")
	}
}

func TestConvertUint8(t *testing.T) {
	validInts := []uint8{0, 1, math.MaxUint8}
	invalidInts := []string{"1.233", "a", "false", strconv.FormatUint(math.MaxUint64, 10)}

	for _, f := range validInts {
		c, err := ConvertUint8(FormatUint8(f))
		if assert.NoError(t, err) {
			assert.EqualValues(t, f, c)
		}
	}
	for _, f := range invalidInts {
		_, err := ConvertUint8(f)
		assert.Error(t, err, "expected '"+f+"' to generate an error")
	}
}

func TestConvertUint16(t *testing.T) {
	validUints := []uint16{0, 1, math.MaxUint8, math.MaxUint16}
	invalidUints := []string{"1.233", "a", "false", strconv.FormatUint(math.MaxUint64, 10)}

	for _, f := range validUints {
		c, err := ConvertUint16(FormatUint16(f))
		if assert.NoError(t, err) {
			assert.EqualValues(t, f, c)
		}
	}
	for _, f := range invalidUints {
		_, err := ConvertUint16(f)
		assert.Error(t, err, "expected '"+f+"' to generate an error")
	}
}

func TestConvertUint32(t *testing.T) {
	validUints := []uint32{0, 1, math.MaxUint8, math.MaxUint16, math.MaxUint32}
	invalidUints := []string{"1.233", "a", "false", strconv.FormatUint(math.MaxUint64, 10)}

	for _, f := range validUints {
		c, err := ConvertUint32(FormatUint32(f))
		if assert.NoError(t, err) {
			assert.EqualValues(t, f, c)
		}
	}
	for _, f := range invalidUints {
		_, err := ConvertUint32(f)
		assert.Error(t, err, "expected '"+f+"' to generate an error")
	}
}

func TestConvertUint64(t *testing.T) {
	validUints := []uint64{0, 1, math.MaxUint8, math.MaxUint16, math.MaxUint32, math.MaxUint64}
	invalidUints := []string{"1.233", "a", "false"}

	for _, f := range validUints {
		c, err := ConvertUint64(FormatUint64(f))
		if assert.NoError(t, err) {
			assert.EqualValues(t, f, c)
		}
	}
	for _, f := range invalidUints {
		_, err := ConvertUint64(f)
		assert.Error(t, err, "expected '"+f+"' to generate an error")
	}
}

func TestIsFloat64AJSONInteger(t *testing.T) {
	assert.False(t, IsFloat64AJSONInteger(math.Inf(1)))
	assert.False(t, IsFloat64AJSONInteger(maxJSONFloat+1))

	assert.False(t, IsFloat64AJSONInteger(minJSONFloat-1))
	assert.True(t, IsFloat64AJSONInteger(1.0))
	assert.True(t, IsFloat64AJSONInteger(maxJSONFloat))
	assert.True(t, IsFloat64AJSONInteger(minJSONFloat))
	assert.True(t, IsFloat64AJSONInteger(1/0.01*67.15000001))
	assert.False(t, IsFloat64AJSONInteger(math.SmallestNonzeroFloat64))
	assert.False(t, IsFloat64AJSONInteger(math.SmallestNonzeroFloat64/2))
	assert.True(t, IsFloat64AJSONInteger(math.SmallestNonzeroFloat64/3))
	assert.True(t, IsFloat64AJSONInteger(math.SmallestNonzeroFloat64/4))
}

func TestFormatBool(t *testing.T) {
	assert.Equal(t, "true", FormatBool(true))
	assert.Equal(t, "false", FormatBool(false))
}
<commit_msg>fix IsFloat64JSONInteger<commit_after>// Copyright 2015 go-swagger maintainers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package swag

import (
	"math"
	"strconv"
	"testing"

	"github.com/stretchr/testify/assert"
)

// These are really dumb tests

func TestConvertBool(t *testing.T) {
	for k := range evaluatesAsTrue {
		r, err := ConvertBool(k)
		if assert.NoError(t, err) {
			assert.True(t, r)
		}
	}
	for _, k := range []string{"a", "", "0", "false", "unchecked"} {
		r, err := ConvertBool(k)
		if assert.NoError(t, err) {
			assert.False(t, r)
		}
	}
}

func TestConvertFloat32(t *testing.T) {
	validFloats := []float32{1.0, -1, math.MaxFloat32, math.SmallestNonzeroFloat32, 0, 5.494430303}
	invalidFloats := []string{"a", strconv.FormatFloat(math.MaxFloat64, 'f', -1, 64), "true"}

	for _, f := range validFloats {
		c, err := ConvertFloat32(FormatFloat32(f))
		if assert.NoError(t, err) {
			assert.EqualValues(t, f, c)
		}
	}
	for _, f := range invalidFloats {
		_, err := ConvertFloat32(f)
		assert.Error(t, err, "expected '"+f+"' to generate an error")
	}
}

func TestConvertFloat64(t *testing.T) {
	validFloats := []float64{1.0, -1, float64(math.MaxFloat32), float64(math.SmallestNonzeroFloat32), math.MaxFloat64, math.SmallestNonzeroFloat64, 0, 5.494430303}
	invalidFloats := []string{"a", "true"}

	for _, f := range validFloats {
		c, err := ConvertFloat64(FormatFloat64(f))
		if assert.NoError(t, err) {
			assert.EqualValues(t, f, c)
		}
	}
	for _, f := range invalidFloats {
		_, err := ConvertFloat64(f)
		assert.Error(t, err, "expected '"+f+"' to generate an error")
	}
}

func TestConvertInt8(t *testing.T) {
	validInts := []int8{0, 1, -1, math.MaxInt8, math.MinInt8}
	invalidInts := []string{"1.233", "a", "false", strconv.FormatInt(int64(math.MaxInt64), 10)}

	for _, f := range validInts {
		c, err := ConvertInt8(FormatInt8(f))
		if assert.NoError(t, err) {
			assert.EqualValues(t, f, c)
		}
	}
	for _, f := range invalidInts {
		_, err := ConvertInt8(f)
		assert.Error(t, err, "expected '"+f+"' to generate an error")
	}
}

func TestConvertInt16(t *testing.T) {
	validInts := []int16{0, 1, -1, math.MaxInt8, math.MinInt8, math.MaxInt16, math.MinInt16}
	invalidInts := []string{"1.233", "a", "false", strconv.FormatInt(int64(math.MaxInt64), 10)}

	for _, f := range validInts {
		c, err := ConvertInt16(FormatInt16(f))
		if assert.NoError(t, err) {
			assert.EqualValues(t, f, c)
		}
	}
	for _, f := range invalidInts {
		_, err := ConvertInt16(f)
		assert.Error(t, err, "expected '"+f+"' to generate an error")
	}
}

func TestConvertInt32(t *testing.T) {
	validInts := []int32{0, 1, -1, math.MaxInt8, math.MinInt8, math.MaxInt16, math.MinInt16, math.MinInt32, math.MaxInt32}
	invalidInts := []string{"1.233", "a", "false", strconv.FormatInt(int64(math.MaxInt64), 10)}

	for _, f := range validInts {
		c, err := ConvertInt32(FormatInt32(f))
		if assert.NoError(t, err) {
			assert.EqualValues(t, f, c)
		}
	}
	for _, f := range invalidInts {
		_, err := ConvertInt32(f)
		assert.Error(t, err, "expected '"+f+"' to generate an error")
	}
}

func TestConvertInt64(t *testing.T) {
	validInts := []int64{0, 1, -1, math.MaxInt8, math.MinInt8, math.MaxInt16, math.MinInt16, math.MinInt32, math.MaxInt32, math.MaxInt64, math.MinInt64}
	invalidInts := []string{"1.233", "a", "false"}

	for _, f := range validInts {
		c, err := ConvertInt64(FormatInt64(f))
		if assert.NoError(t, err) {
			assert.EqualValues(t, f, c)
		}
	}
	for _, f := range invalidInts {
		_, err := ConvertInt64(f)
		assert.Error(t, err, "expected '"+f+"' to generate an error")
	}
}

func TestConvertUint8(t *testing.T) {
	validInts := []uint8{0, 1, math.MaxUint8}
	invalidInts := []string{"1.233", "a", "false", strconv.FormatUint(math.MaxUint64, 10)}

	for _, f := range validInts {
		c, err := ConvertUint8(FormatUint8(f))
		if assert.NoError(t, err) {
			assert.EqualValues(t, f, c)
		}
	}
	for _, f := range invalidInts {
		_, err := ConvertUint8(f)
		assert.Error(t, err, "expected '"+f+"' to generate an error")
	}
}

func TestConvertUint16(t *testing.T) {
	validUints := []uint16{0, 1, math.MaxUint8, math.MaxUint16}
	invalidUints := []string{"1.233", "a", "false", strconv.FormatUint(math.MaxUint64, 10)}

	for _, f := range validUints {
		c, err := ConvertUint16(FormatUint16(f))
		if assert.NoError(t, err) {
			assert.EqualValues(t, f, c)
		}
	}
	for _, f := range invalidUints {
		_, err := ConvertUint16(f)
		assert.Error(t, err, "expected '"+f+"' to generate an error")
	}
}

func TestConvertUint32(t *testing.T) {
	validUints := []uint32{0, 1, math.MaxUint8, math.MaxUint16, math.MaxUint32}
	invalidUints := []string{"1.233", "a", "false", strconv.FormatUint(math.MaxUint64, 10)}

	for _, f := range validUints {
		c, err := ConvertUint32(FormatUint32(f))
		if assert.NoError(t, err) {
			assert.EqualValues(t, f, c)
		}
	}
	for _, f := range invalidUints {
		_, err := ConvertUint32(f)
		assert.Error(t, err, "expected '"+f+"' to generate an error")
	}
}

func TestConvertUint64(t *testing.T) {
	validUints := []uint64{0, 1, math.MaxUint8, math.MaxUint16, math.MaxUint32, math.MaxUint64}
	invalidUints := []string{"1.233", "a", "false"}

	for _, f := range validUints {
		c, err := ConvertUint64(FormatUint64(f))
		if assert.NoError(t, err) {
			assert.EqualValues(t, f, c)
		}
	}
	for _, f := range invalidUints {
		_, err := ConvertUint64(f)
		assert.Error(t, err, "expected '"+f+"' to generate an error")
	}
}

func TestIsFloat64AJSONInteger(t *testing.T) {
	assert.False(t, IsFloat64AJSONInteger(math.Inf(1)))
	assert.False(t, IsFloat64AJSONInteger(maxJSONFloat+1))

	assert.False(t, IsFloat64AJSONInteger(minJSONFloat-1))
	assert.True(t, IsFloat64AJSONInteger(1.0))
	assert.True(t, IsFloat64AJSONInteger(maxJSONFloat))
	assert.True(t, IsFloat64AJSONInteger(minJSONFloat))
	assert.True(t, IsFloat64AJSONInteger(1/0.01*67.15000001))
	assert.False(t, IsFloat64AJSONInteger(math.SmallestNonzeroFloat64))
	assert.True(t, IsFloat64AJSONInteger(math.SmallestNonzeroFloat64/2))
	assert.True(t, IsFloat64AJSONInteger(math.SmallestNonzeroFloat64/3))
	assert.True(t, IsFloat64AJSONInteger(math.SmallestNonzeroFloat64/4))
}

func TestFormatBool(t *testing.T) {
	assert.Equal(t, "true", FormatBool(true))
	assert.Equal(t, "false", FormatBool(false))
}
<|endoftext|>


<commit_before>// Copyright 2017 Frédéric Guillot. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.

package sanitizer // import "miniflux.app/reader/sanitizer"

import (
	"bytes"
	"fmt"
	"io"
	"regexp"
	"strconv"
	"strings"

	"miniflux.app/url"

	"golang.org/x/net/html"
)

var (
	youtubeEmbedRegex = regexp.MustCompile(`//www\.youtube\.com/embed/(.*)`)
	splitSrcsetRegex  = regexp.MustCompile(`,\s+`)
)

// Sanitize returns safe HTML.
func Sanitize(baseURL, input string) string {
	var buffer bytes.Buffer
	var tagStack []string
	var parentTag string
	blacklistedTagDepth := 0

	tokenizer := html.NewTokenizer(bytes.NewBufferString(input))
	for {
		if tokenizer.Next() == html.ErrorToken {
			err := tokenizer.Err()
			if err == io.EOF {
				return buffer.String()
			}

			return ""
		}

		token := tokenizer.Token()
		switch token.Type {
		case html.TextToken:
			if blacklistedTagDepth > 0 {
				continue
			}

			// An iframe element never has fallback content.
			// See https://www.w3.org/TR/2010/WD-html5-20101019/the-iframe-element.html#the-iframe-element
			if parentTag == "iframe" {
				continue
			}

			buffer.WriteString(html.EscapeString(token.Data))
		case html.StartTagToken:
			tagName := token.DataAtom.String()
			parentTag = tagName

			if !isPixelTracker(tagName, token.Attr) && isValidTag(tagName) {
				attrNames, htmlAttributes := sanitizeAttributes(baseURL, tagName, token.Attr)

				if hasRequiredAttributes(tagName, attrNames) {
					if len(attrNames) > 0 {
						buffer.WriteString("<" + tagName + " " + htmlAttributes + ">")
					} else {
						buffer.WriteString("<" + tagName + ">")
					}

					tagStack = append(tagStack, tagName)
				}
			} else if isBlockedTag(tagName) {
				blacklistedTagDepth++
			}
		case html.EndTagToken:
			tagName := token.DataAtom.String()
			if isValidTag(tagName) && inList(tagName, tagStack) {
				buffer.WriteString(fmt.Sprintf("</%s>", tagName))
			} else if isBlockedTag(tagName) {
				blacklistedTagDepth--
			}
		case html.SelfClosingTagToken:
			tagName := token.DataAtom.String()
			if !isPixelTracker(tagName, token.Attr) && isValidTag(tagName) {
				attrNames, htmlAttributes := sanitizeAttributes(baseURL, tagName, token.Attr)

				if hasRequiredAttributes(tagName, attrNames) {
					if len(attrNames) > 0 {
						buffer.WriteString("<" + tagName + " " + htmlAttributes + "/>")
					} else {
						buffer.WriteString("<" + tagName + "/>")
					}
				}
			}
		}
	}
}

func sanitizeAttributes(baseURL, tagName string, attributes []html.Attribute) ([]string, string) {
	var htmlAttrs, attrNames []string
	var err error

	for _, attribute := range attributes {
		value := attribute.Val

		if !isValidAttribute(tagName, attribute.Key) {
			continue
		}

		if (tagName == "img" || tagName == "source") && attribute.Key == "srcset" {
			value = sanitizeSrcsetAttr(baseURL, value)
		}

		if isExternalResourceAttribute(attribute.Key) {
			if tagName == "iframe" {
				if isValidIframeSource(baseURL, attribute.Val) {
					value = rewriteIframeURL(attribute.Val)
				} else {
					continue
				}
			} else if tagName == "img" && attribute.Key == "src" && isValidDataAttribute(attribute.Val) {
				value = attribute.Val
			} else {
				value, err = url.AbsoluteURL(baseURL, value)
				if err != nil {
					continue
				}

				if !hasValidURIScheme(value) || isBlockedResource(value) {
					continue
				}
			}
		}

		attrNames = append(attrNames, attribute.Key)
		htmlAttrs = append(htmlAttrs, fmt.Sprintf(`%s="%s"`, attribute.Key, html.EscapeString(value)))
	}

	extraAttrNames, extraHTMLAttributes := getExtraAttributes(tagName)
	if len(extraAttrNames) > 0 {
		attrNames = append(attrNames, extraAttrNames...)
		htmlAttrs = append(htmlAttrs, extraHTMLAttributes...)
	}

	return attrNames, strings.Join(htmlAttrs, " ")
}

func getExtraAttributes(tagName string) ([]string, []string) {
	switch tagName {
	case "a":
		return []string{"rel", "target", "referrerpolicy"}, []string{`rel="noopener noreferrer"`, `target="_blank"`, `referrerpolicy="no-referrer"`}
	case "video", "audio":
		return []string{"controls"}, []string{"controls"}
	case "iframe":
		return []string{"sandbox", "loading"}, []string{`sandbox="allow-scripts allow-same-origin allow-popups"`, `loading="lazy"`}
	case "img":
		return []string{"loading"}, []string{`loading="lazy"`}
	default:
		return nil, nil
	}
}

func isValidTag(tagName string) bool {
	for element := range getTagAllowList() {
		if tagName == element {
			return true
		}
	}

	return false
}

func isValidAttribute(tagName, attributeName string) bool {
	for element, attributes := range getTagAllowList() {
		if tagName == element {
			if inList(attributeName, attributes) {
				return true
			}
		}
	}

	return false
}

func isExternalResourceAttribute(attribute string) bool {
	switch attribute {
	case "src", "href", "poster", "cite":
		return true
	default:
		return false
	}
}

func isPixelTracker(tagName string, attributes []html.Attribute) bool {
	if tagName == "img" {
		hasHeight := false
		hasWidth := false

		for _, attribute := range attributes {
			if attribute.Key == "height" && attribute.Val == "1" {
				hasHeight = true
			}

			if attribute.Key == "width" && attribute.Val == "1" {
				hasWidth = true
			}
		}

		return hasHeight && hasWidth
	}

	return false
}

func hasRequiredAttributes(tagName string, attributes []string) bool {
	elements := make(map[string][]string)
	elements["a"] = []string{"href"}
	elements["iframe"] = []string{"src"}
	elements["img"] = []string{"src"}
	elements["source"] = []string{"src", "srcset"}

	for element, attrs := range elements {
		if tagName == element {
			for _, attribute := range attributes {
				for _, attr := range attrs {
					if attr == attribute {
						return true
					}
				}
			}

			return false
		}
	}

	return true
}

// See https://www.iana.org/assignments/uri-schemes/uri-schemes.xhtml
func hasValidURIScheme(src string) bool {
	whitelist := []string{
		"apt:",
		"bitcoin:",
		"callto:",
		"dav:",
		"davs:",
		"ed2k://",
		"facetime://",
		"feed:",
		"ftp://",
		"geo:",
		"gopher://",
		"git://",
		"http://",
		"https://",
		"irc://",
		"irc6://",
		"ircs://",
		"itms://",
		"itms-apps://",
		"magnet:",
		"mailto:",
		"news:",
		"nntp:",
		"rtmp://",
		"sip:",
		"sips:",
		"skype:",
		"spotify:",
		"ssh://",
		"sftp://",
		"steam://",
		"svn://",
		"svn+ssh://",
		"tel:",
		"webcal://",
		"xmpp:",
	}

	for _, prefix := range whitelist {
		if strings.HasPrefix(src, prefix) {
			return true
		}
	}

	return false
}

func isBlockedResource(src string) bool {
	blacklist := []string{
		"feedsportal.com",
		"api.flattr.com",
		"stats.wordpress.com",
		"plus.google.com/share",
		"twitter.com/share",
		"feeds.feedburner.com",
	}

	for _, element := range blacklist {
		if strings.Contains(src, element) {
			return true
		}
	}

	return false
}

func isValidIframeSource(baseURL, src string) bool {
	whitelist := []string{
		"https://invidio.us",
		"//www.youtube.com",
		"http://www.youtube.com",
		"https://www.youtube.com",
		"https://www.youtube-nocookie.com",
		"http://player.vimeo.com",
		"https://player.vimeo.com",
		"http://www.dailymotion.com",
		"https://www.dailymotion.com",
		"http://vk.com",
		"https://vk.com",
		"http://soundcloud.com",
		"https://soundcloud.com",
		"http://w.soundcloud.com",
		"https://w.soundcloud.com",
		"http://bandcamp.com",
		"https://bandcamp.com",
		"https://cdn.embedly.com",
	}

	// allow iframe from same origin
	if url.Domain(baseURL) == url.Domain(src) {
		return true
	}

	for _, prefix := range whitelist {
		if strings.HasPrefix(src, prefix) {
			return true
		}
	}

	return false
}

func getTagAllowList() map[string][]string {
	whitelist := make(map[string][]string)
	whitelist["img"] = []string{"alt", "title", "src", "srcset", "sizes"}
	whitelist["picture"] = []string{}
	whitelist["audio"] = []string{"src"}
	whitelist["video"] = []string{"poster", "height", "width", "src"}
	whitelist["source"] = []string{"src", "type", "srcset", "sizes", "media"}
	whitelist["dt"] = []string{}
	whitelist["dd"] = []string{}
	whitelist["dl"] = []string{}
	whitelist["table"] = []string{}
	whitelist["caption"] = []string{}
	whitelist["thead"] = []string{}
	whitelist["tfooter"] = []string{}
	whitelist["tr"] = []string{}
	whitelist["td"] = []string{"rowspan", "colspan"}
	whitelist["th"] = []string{"rowspan", "colspan"}
	whitelist["h1"] = []string{}
	whitelist["h2"] = []string{}
	whitelist["h3"] = []string{}
	whitelist["h4"] = []string{}
	whitelist["h5"] = []string{}
	whitelist["h6"] = []string{}
	whitelist["strong"] = []string{}
	whitelist["em"] = []string{}
	whitelist["code"] = []string{}
	whitelist["pre"] = []string{}
	whitelist["blockquote"] = []string{}
	whitelist["q"] = []string{"cite"}
	whitelist["p"] = []string{}
	whitelist["ul"] = []string{}
	whitelist["li"] = []string{}
	whitelist["ol"] = []string{}
	whitelist["br"] = []string{}
	whitelist["del"] = []string{}
	whitelist["a"] = []string{"href", "title"}
	whitelist["figure"] = []string{}
	whitelist["figcaption"] = []string{}
	whitelist["cite"] = []string{}
	whitelist["time"] = []string{"datetime"}
	whitelist["abbr"] = []string{"title"}
	whitelist["acronym"] = []string{"title"}
	whitelist["wbr"] = []string{}
	whitelist["dfn"] = []string{}
	whitelist["sub"] = []string{}
	whitelist["sup"] = []string{}
	whitelist["var"] = []string{}
	whitelist["samp"] = []string{}
	whitelist["s"] = []string{}
	whitelist["del"] = []string{}
	whitelist["ins"] = []string{}
	whitelist["kbd"] = []string{}
	whitelist["rp"] = []string{}
	whitelist["rt"] = []string{}
	whitelist["rtc"] = []string{}
	whitelist["ruby"] = []string{}
	whitelist["iframe"] = []string{"width", "height", "frameborder", "src", "allowfullscreen"}
	return whitelist
}

func inList(needle string, haystack []string) bool {
	for _, element := range haystack {
		if element == needle {
			return true
		}
	}

	return false
}

func rewriteIframeURL(link string) string {
	matches := youtubeEmbedRegex.FindStringSubmatch(link)
	if len(matches) == 2 {
		return `https://www.youtube-nocookie.com/embed/` + matches[1]
	}

	return link
}

func isBlockedTag(tagName string) bool {
	blacklist := []string{
		"noscript",
		"script",
		"style",
	}

	for _, element := range blacklist {
		if element == tagName {
			return true
		}
	}

	return false
}

/*

One or more strings separated by commas, indicating possible image sources for the user agent to use.

Each string is composed of:
- A URL to an image
- Optionally, whitespace followed by one of:
- A width descriptor (a positive integer directly followed by w). The width descriptor is divided by the source size given in the sizes attribute to calculate the effective pixel density.
- A pixel density descriptor (a positive floating point number directly followed by x).

*/
func sanitizeSrcsetAttr(baseURL, value string) string {
	var sanitizedSources []string
	rawSources := splitSrcsetRegex.Split(value, -1)
	for _, rawSource := range rawSources {
		parts := strings.Split(strings.TrimSpace(rawSource), " ")
		nbParts := len(parts)

		if nbParts > 0 {
			sanitizedSource := parts[0]
			if !strings.HasPrefix(parts[0], "data:") {
				var err error
				sanitizedSource, err = url.AbsoluteURL(baseURL, parts[0])
				if err != nil {
					continue
				}
			}

			if nbParts == 2 && isValidWidthOrDensityDescriptor(parts[1]) {
				sanitizedSource += " " + parts[1]
			}

			sanitizedSources = append(sanitizedSources, sanitizedSource)
		}
	}
	return strings.Join(sanitizedSources, ", ")
}

func isValidWidthOrDensityDescriptor(value string) bool {
	if value == "" {
		return false
	}

	lastChar := value[len(value)-1:]
	if lastChar != "w" && lastChar != "x" {
		return false
	}

	_, err := strconv.ParseFloat(value[0:len(value)-1], 32)
	return err == nil
}

func isValidDataAttribute(value string) bool {
	var dataAttributeAllowList = []string{
		"data:image/avif",
		"data:image/apng",
		"data:image/png",
		"data:image/svg",
		"data:image/svg+xml",
		"data:image/jpg",
		"data:image/jpeg",
		"data:image/gif",
		"data:image/webp",
	}

	for _, prefix := range dataAttributeAllowList {
		if strings.HasPrefix(value, prefix) {
			return true
		}
	}
	return false
}
<commit_msg>update whitelist fix bilibili video<commit_after>// Copyright 2017 Frédéric Guillot. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.

package sanitizer // import "miniflux.app/reader/sanitizer"

import (
	"bytes"
	"fmt"
	"io"
	"regexp"
	"strconv"
	"strings"

	"miniflux.app/url"

	"golang.org/x/net/html"
)

var (
	youtubeEmbedRegex = regexp.MustCompile(`//www\.youtube\.com/embed/(.*)`)
	splitSrcsetRegex  = regexp.MustCompile(`,\s+`)
)

// Sanitize returns safe HTML.
func Sanitize(baseURL, input string) string {
	var buffer bytes.Buffer
	var tagStack []string
	var parentTag string
	blacklistedTagDepth := 0

	tokenizer := html.NewTokenizer(bytes.NewBufferString(input))
	for {
		if tokenizer.Next() == html.ErrorToken {
			err := tokenizer.Err()
			if err == io.EOF {
				return buffer.String()
			}

			return ""
		}

		token := tokenizer.Token()
		switch token.Type {
		case html.TextToken:
			if blacklistedTagDepth > 0 {
				continue
			}

			// An iframe element never has fallback content.
			// See https://www.w3.org/TR/2010/WD-html5-20101019/the-iframe-element.html#the-iframe-element
			if parentTag == "iframe" {
				continue
			}

			buffer.WriteString(html.EscapeString(token.Data))
		case html.StartTagToken:
			tagName := token.DataAtom.String()
			parentTag = tagName

			if !isPixelTracker(tagName, token.Attr) && isValidTag(tagName) {
				attrNames, htmlAttributes := sanitizeAttributes(baseURL, tagName, token.Attr)

				if hasRequiredAttributes(tagName, attrNames) {
					if len(attrNames) > 0 {
						buffer.WriteString("<" + tagName + " " + htmlAttributes + ">")
					} else {
						buffer.WriteString("<" + tagName + ">")
					}

					tagStack = append(tagStack, tagName)
				}
			} else if isBlockedTag(tagName) {
				blacklistedTagDepth++
			}
		case html.EndTagToken:
			tagName := token.DataAtom.String()
			if isValidTag(tagName) && inList(tagName, tagStack) {
				buffer.WriteString(fmt.Sprintf("</%s>", tagName))
			} else if isBlockedTag(tagName) {
				blacklistedTagDepth--
			}
		case html.SelfClosingTagToken:
			tagName := token.DataAtom.String()
			if !isPixelTracker(tagName, token.Attr) && isValidTag(tagName) {
				attrNames, htmlAttributes := sanitizeAttributes(baseURL, tagName, token.Attr)

				if hasRequiredAttributes(tagName, attrNames) {
					if len(attrNames) > 0 {
						buffer.WriteString("<" + tagName + " " + htmlAttributes + "/>")
					} else {
						buffer.WriteString("<" + tagName + "/>")
					}
				}
			}
		}
	}
}

func sanitizeAttributes(baseURL, tagName string, attributes []html.Attribute) ([]string, string) {
	var htmlAttrs, attrNames []string
	var err error

	for _, attribute := range attributes {
		value := attribute.Val

		if !isValidAttribute(tagName, attribute.Key) {
			continue
		}

		if (tagName == "img" || tagName == "source") && attribute.Key == "srcset" {
			value = sanitizeSrcsetAttr(baseURL, value)
		}

		if isExternalResourceAttribute(attribute.Key) {
			if tagName == "iframe" {
				if isValidIframeSource(baseURL, attribute.Val) {
					value = rewriteIframeURL(attribute.Val)
				} else {
					continue
				}
			} else if tagName == "img" && attribute.Key == "src" && isValidDataAttribute(attribute.Val) {
				value = attribute.Val
			} else {
				value, err = url.AbsoluteURL(baseURL, value)
				if err != nil {
					continue
				}

				if !hasValidURIScheme(value) || isBlockedResource(value) {
					continue
				}
			}
		}

		attrNames = append(attrNames, attribute.Key)
		htmlAttrs = append(htmlAttrs, fmt.Sprintf(`%s="%s"`, attribute.Key, html.EscapeString(value)))
	}

	extraAttrNames, extraHTMLAttributes := getExtraAttributes(tagName)
	if len(extraAttrNames) > 0 {
		attrNames = append(attrNames, extraAttrNames...)
		htmlAttrs = append(htmlAttrs, extraHTMLAttributes...)
	}

	return attrNames, strings.Join(htmlAttrs, " ")
}

func getExtraAttributes(tagName string) ([]string, []string) {
	switch tagName {
	case "a":
		return []string{"rel", "target", "referrerpolicy"}, []string{`rel="noopener noreferrer"`, `target="_blank"`, `referrerpolicy="no-referrer"`}
	case "video", "audio":
		return []string{"controls"}, []string{"controls"}
	case "iframe":
		return []string{"sandbox", "loading"}, []string{`sandbox="allow-scripts allow-same-origin allow-popups"`, `loading="lazy"`}
	case "img":
		return []string{"loading"}, []string{`loading="lazy"`}
	default:
		return nil, nil
	}
}

func isValidTag(tagName string) bool {
	for element := range getTagAllowList() {
		if tagName == element {
			return true
		}
	}

	return false
}

func isValidAttribute(tagName, attributeName string) bool {
	for element, attributes := range getTagAllowList() {
		if tagName == element {
			if inList(attributeName, attributes) {
				return true
			}
		}
	}

	return false
}

func isExternalResourceAttribute(attribute string) bool {
	switch attribute {
	case "src", "href", "poster", "cite":
		return true
	default:
		return false
	}
}

func isPixelTracker(tagName string, attributes []html.Attribute) bool {
	if tagName == "img" {
		hasHeight := false
		hasWidth := false

		for _, attribute := range attributes {
			if attribute.Key == "height" && attribute.Val == "1" {
				hasHeight = true
			}

			if attribute.Key == "width" && attribute.Val == "1" {
				hasWidth = true
			}
		}

		return hasHeight && hasWidth
	}

	return false
}

func hasRequiredAttributes(tagName string, attributes []string) bool {
	elements := make(map[string][]string)
	elements["a"] = []string{"href"}
	elements["iframe"] = []string{"src"}
	elements["img"] = []string{"src"}
	elements["source"] = []string{"src", "srcset"}

	for element, attrs := range elements {
		if tagName == element {
			for _, attribute := range attributes {
				for _, attr := range attrs {
					if attr == attribute {
						return true
					}
				}
			}

			return false
		}
	}

	return true
}

// See https://www.iana.org/assignments/uri-schemes/uri-schemes.xhtml
func hasValidURIScheme(src string) bool {
	whitelist := []string{
		"apt:",
		"bitcoin:",
		"callto:",
		"dav:",
		"davs:",
		"ed2k://",
		"facetime://",
		"feed:",
		"ftp://",
		"geo:",
		"gopher://",
		"git://",
		"http://",
		"https://",
		"irc://",
		"irc6://",
		"ircs://",
		"itms://",
		"itms-apps://",
		"magnet:",
		"mailto:",
		"news:",
		"nntp:",
		"rtmp://",
		"sip:",
		"sips:",
		"skype:",
		"spotify:",
		"ssh://",
		"sftp://",
		"steam://",
		"svn://",
		"svn+ssh://",
		"tel:",
		"webcal://",
		"xmpp:",
	}

	for _, prefix := range whitelist {
		if strings.HasPrefix(src, prefix) {
			return true
		}
	}

	return false
}

func isBlockedResource(src string) bool {
	blacklist := []string{
		"feedsportal.com",
		"api.flattr.com",
		"stats.wordpress.com",
		"plus.google.com/share",
		"twitter.com/share",
		"feeds.feedburner.com",
	}

	for _, element := range blacklist {
		if strings.Contains(src, element) {
			return true
		}
	}

	return false
}

func isValidIframeSource(baseURL, src string) bool {
	whitelist := []string{
		"https://invidio.us",
		"//www.youtube.com",
		"http://www.youtube.com",
		"https://www.youtube.com",
		"https://www.youtube-nocookie.com",
		"http://player.vimeo.com",
		"https://player.vimeo.com",
		"http://www.dailymotion.com",
		"https://www.dailymotion.com",
		"http://vk.com",
		"https://vk.com",
		"http://soundcloud.com",
		"https://soundcloud.com",
		"http://w.soundcloud.com",
		"https://w.soundcloud.com",
		"http://bandcamp.com",
		"https://bandcamp.com",
		"https://cdn.embedly.com",
		"https://player.bilibili.com",
	}

	// allow iframe from same origin
	if url.Domain(baseURL) == url.Domain(src) {
		return true
	}

	for _, prefix := range whitelist {
		if strings.HasPrefix(src, prefix) {
			return true
		}
	}

	return false
}

func getTagAllowList() map[string][]string {
	whitelist := make(map[string][]string)
	whitelist["img"] = []string{"alt", "title", "src", "srcset", "sizes"}
	whitelist["picture"] = []string{}
	whitelist["audio"] = []string{"src"}
	whitelist["video"] = []string{"poster", "height", "width", "src"}
	whitelist["source"] = []string{"src", "type", "srcset", "sizes", "media"}
	whitelist["dt"] = []string{}
	whitelist["dd"] = []string{}
	whitelist["dl"] = []string{}
	whitelist["table"] = []string{}
	whitelist["caption"] = []string{}
	whitelist["thead"] = []string{}
	whitelist["tfooter"] = []string{}
	whitelist["tr"] = []string{}
	whitelist["td"] = []string{"rowspan", "colspan"}
	whitelist["th"] = []string{"rowspan", "colspan"}
	whitelist["h1"] = []string{}
	whitelist["h2"] = []string{}
	whitelist["h3"] = []string{}
	whitelist["h4"] = []string{}
	whitelist["h5"] = []string{}
	whitelist["h6"] = []string{}
	whitelist["strong"] = []string{}
	whitelist["em"] = []string{}
	whitelist["code"] = []string{}
	whitelist["pre"] = []string{}
	whitelist["blockquote"] = []string{}
	whitelist["q"] = []string{"cite"}
	whitelist["p"] = []string{}
	whitelist["ul"] = []string{}
	whitelist["li"] = []string{}
	whitelist["ol"] = []string{}
	whitelist["br"] = []string{}
	whitelist["del"] = []string{}
	whitelist["a"] = []string{"href", "title"}
	whitelist["figure"] = []string{}
	whitelist["figcaption"] = []string{}
	whitelist["cite"] = []string{}
	whitelist["time"] = []string{"datetime"}
	whitelist["abbr"] = []string{"title"}
	whitelist["acronym"] = []string{"title"}
	whitelist["wbr"] = []string{}
	whitelist["dfn"] = []string{}
	whitelist["sub"] = []string{}
	whitelist["sup"] = []string{}
	whitelist["var"] = []string{}
	whitelist["samp"] = []string{}
	whitelist["s"] = []string{}
	whitelist["del"] = []string{}
	whitelist["ins"] = []string{}
	whitelist["kbd"] = []string{}
	whitelist["rp"] = []string{}
	whitelist["rt"] = []string{}
	whitelist["rtc"] = []string{}
	whitelist["ruby"] = []string{}
	whitelist["iframe"] = []string{"width", "height", "frameborder", "src", "allowfullscreen"}
	return whitelist
}

func inList(needle string, haystack []string) bool {
	for _, element := range haystack {
		if element == needle {
			return true
		}
	}

	return false
}

func rewriteIframeURL(link string) string {
	matches := youtubeEmbedRegex.FindStringSubmatch(link)
	if len(matches) == 2 {
		return `https://www.youtube-nocookie.com/embed/` + matches[1]
	}

	return link
}

func isBlockedTag(tagName string) bool {
	blacklist := []string{
		"noscript",
		"script",
		"style",
	}

	for _, element := range blacklist {
		if element == tagName {
			return true
		}
	}

	return false
}

/*

One or more strings separated by commas, indicating possible image sources for the user agent to use.

Each string is composed of:
- A URL to an image
- Optionally, whitespace followed by one of:
- A width descriptor (a positive integer directly followed by w). The width descriptor is divided by the source size given in the sizes attribute to calculate the effective pixel density.
- A pixel density descriptor (a positive floating point number directly followed by x).

*/
func sanitizeSrcsetAttr(baseURL, value string) string {
	var sanitizedSources []string
	rawSources := splitSrcsetRegex.Split(value, -1)
	for _, rawSource := range rawSources {
		parts := strings.Split(strings.TrimSpace(rawSource), " ")
		nbParts := len(parts)

		if nbParts > 0 {
			sanitizedSource := parts[0]
			if !strings.HasPrefix(parts[0], "data:") {
				var err error
				sanitizedSource, err = url.AbsoluteURL(baseURL, parts[0])
				if err != nil {
					continue
				}
			}

			if nbParts == 2 && isValidWidthOrDensityDescriptor(parts[1]) {
				sanitizedSource += " " + parts[1]
			}

			sanitizedSources = append(sanitizedSources, sanitizedSource)
		}
	}
	return strings.Join(sanitizedSources, ", ")
}

func isValidWidthOrDensityDescriptor(value string) bool {
	if value == "" {
		return false
	}

	lastChar := value[len(value)-1:]
	if lastChar != "w" && lastChar != "x" {
		return false
	}

	_, err := strconv.ParseFloat(value[0:len(value)-1], 32)
	return err == nil
}

func isValidDataAttribute(value string) bool {
	var dataAttributeAllowList = []string{
		"data:image/avif",
		"data:image/apng",
		"data:image/png",
		"data:image/svg",
		"data:image/svg+xml",
		"data:image/jpg",
		"data:image/jpeg",
		"data:image/gif",
		"data:image/webp",
	}

	for _, prefix := range dataAttributeAllowList {
		if strings.HasPrefix(value, prefix) {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>package utils

import (
	"testing"
)

func TestSubdomainRegex(t *testing.T) {
	tests := []struct {
		name     string
		domain   string
		event    string
		expected string
	}{

		{"Test1: Subdomain", "owasp.org", "subdomain.owasp.org", "subdomain.owasp.org"},
		{"Test2: Nested subdomain", "owasp.org", "sub.subdomain.owasp.org", "sub.subdomain.owasp.org"},
		{"Test3: Subdomain-dashes", "owasp.org", "sub-domain.owasp.org", "sub-domain.owasp.org"},
		{"Test4: Subdomain-dashes again", "owasp.org", "sub-d.sub-domain.owasp.org", "sub-d.sub-domain.owasp.org"},
		{"Test5: Double period", "owasp.org", "sub..owasp.org", ""},
		{"Test6: Wrong domain", "owasp.org", ".sub-d.sub-domain.owasp.com", ""},
		{"Test7: Sub end with dash", "owasp.org", "sub-.owasp.org", ""},
	}
	for _, tt := range tests {
		s := SubdomainRegex(tt.domain)
		result := s.FindString(tt.event)
		if result != tt.expected {
			t.Errorf("Error Event %s: regex did not match %s", tt.name, tt.event)

		}

	}

}

func TestNewUniqueElements(t *testing.T) {
	tests := []struct {
		name     string
		orig     []string
		event    []string
		expected []string
	}{
		{"Test 1: Duplicate elements", []string{"sub1.owasp.org", "sub2.owasp.org", "sub3.owasp.org"}, []string{"sub4.owasp.org", "sub4.owasp.org"}, []string{"sub4.owasp.org"}},
		{"Test 2: Empty return", []string{"sub1.owasp.org", "sub2.owasp.org", "sub3.owasp.org"}, []string{"sub1.owasp.org"}, []string{}},
	}

	for _, tt := range tests {
		s := NewUniqueElements(tt.orig, tt.event...)
		for v := range s {
			if s[v] != tt.expected[v] {
				t.Errorf("Error Event %s: got %s, expected %s", tt.name, s, tt.expected)
			}

		}
	}
}

func TestUniqueAppend(t *testing.T) {
	tests := []struct {
		name     string
		orig     []string
		event    string
		expected []string
	}{
		{"Test 1: Duplicate elements", []string{"sub1.owasp.org", "sub2.owasp.org"}, "sub2.owasp.org", []string{"sub1.owasp.org", "sub2.owasp.org"}},
	}
	for _, tt := range tests {
		s := UniqueAppend(tt.orig, tt.event)
		i := 0
		for _, x := range s {
			if x != tt.expected[i] {
				t.Errorf("Error in %s, got %s, expected %s.", tt.name, x, tt.expected[i])
			}
			i++
		}
	}
}

func TestRemoveAsteriskLabel(t *testing.T) {
	tests := []struct {
		name     string
		event    string
		expected string
	}{

		{"Test1: Subdomain", "*.subdomain.owasp.org", "subdomain.owasp.org"},
		{"Test2: Nested subdomain", "*.subdomain.owasp.org", "subdomain.owasp.org"},
		{"Test3: Subdomain-dashes", "*.sub-domain.owasp.org", "sub-domain.owasp.org"},
		{"Test4: Subdomain-dashes", "*.sub-d.sub-domain.owasp.org", "sub-d.sub-domain.owasp.org"},
	}
	for _, tt := range tests {
		s := RemoveAsteriskLabel(tt.event)
		if s != tt.expected && s != "" {
			t.Errorf("Error Event %s: was expecting \"\" or %s, got %s", tt.name, tt.expected, tt.event)
		}

	}

}
<commit_msg>Update misc_test.go<commit_after>package utils

import (
	"testing"
)

func TestSubdomainRegex(t *testing.T) {
	tests := []struct {
		name     string
		domain   string
		event    string
		expected string
	}{

		{"Test 1: Subdomain", "owasp.org", "subdomain.owasp.org", "subdomain.owasp.org"},
		{"Test 2: Nested subdomain", "owasp.org", "sub.subdomain.owasp.org", "sub.subdomain.owasp.org"},
		{"Test 3: Subdomain-dashes", "owasp.org", "sub-domain.owasp.org", "sub-domain.owasp.org"},
		{"Test 4: Subdomain-dashes again", "owasp.org", "sub-d.sub-domain.owasp.org", "sub-d.sub-domain.owasp.org"},
		{"Test 5: Double period", "owasp.org", "sub..owasp.org", ""},
		{"Test 6: Wrong domain", "owasp.org", ".sub-d.sub-domain.owasp.com", ""},
		{"Test 7: Sub end with dash", "owasp.org", "sub-.owasp.org", ""},
	}
	for _, tt := range tests {
		s := SubdomainRegex(tt.domain)
		result := s.FindString(tt.event)
		if result != tt.expected {
			t.Errorf("Error Event %s: regex did not match %s", tt.name, tt.event)

		}

	}

}

func TestNewUniqueElements(t *testing.T) {
	tests := []struct {
		name     string
		orig     []string
		event    []string
		expected []string
	}{
		{"Test 1: Duplicate elements", []string{"sub1.owasp.org", "sub2.owasp.org", "sub3.owasp.org"}, []string{"sub4.owasp.org", "sub4.owasp.org"}, []string{"sub4.owasp.org"}},
		{"Test 2: Empty return", []string{"sub1.owasp.org", "sub2.owasp.org", "sub3.owasp.org"}, []string{"sub1.owasp.org"}, []string{}},
	}

	for _, tt := range tests {
		s := NewUniqueElements(tt.orig, tt.event...)
		for v := range s {
			if s[v] != tt.expected[v] {
				t.Errorf("Error Event %s: got %s, expected %s", tt.name, s, tt.expected)
			}

		}
	}
}

func TestUniqueAppend(t *testing.T) {
	tests := []struct {
		name     string
		orig     []string
		event    string
		expected []string
	}{
		{"Test 1: Duplicate elements", []string{"sub1.owasp.org", "sub2.owasp.org"}, "sub2.owasp.org", []string{"sub1.owasp.org", "sub2.owasp.org"}},
	}
	for _, tt := range tests {
		s := UniqueAppend(tt.orig, tt.event)
		i := 0
		for _, x := range s {
			if x != tt.expected[i] {
				t.Errorf("Error in %s, got %s, expected %s.", tt.name, x, tt.expected[i])
			}
			i++
		}
	}
}

func TestRemoveAsteriskLabel(t *testing.T) {
	tests := []struct {
		name     string
		event    string
		expected string
	}{

		{"Test 1: Subdomain", "*.subdomain.owasp.org", "subdomain.owasp.org"},
		{"Test 2: Nested subdomain", "*.subdomain.owasp.org", "subdomain.owasp.org"},
		{"Test 3: Subdomain-dashes", "*.sub-domain.owasp.org", "sub-domain.owasp.org"},
		{"Test 4: Subdomain-dashes", "*.sub-d.sub-domain.owasp.org", "sub-d.sub-domain.owasp.org"},
	}
	for _, tt := range tests {
		s := RemoveAsteriskLabel(tt.event)
		if s != tt.expected && s != "" {
			t.Errorf("Error Event %s: was expecting \"\" or %s, got %s", tt.name, tt.expected, tt.event)
		}

	}

}
<|endoftext|>


<commit_before>package app

import (
	"encoding/json"
	"fmt"
	"github.com/DanielRenne/GoCore/core/dbServices"
	"github.com/DanielRenne/GoCore/core/fileCache"
	"github.com/DanielRenne/GoCore/core/ginServer"
	"github.com/DanielRenne/GoCore/core/serverSettings"
	"github.com/gin-gonic/gin"
	"github.com/gorilla/websocket"
	"log"
	"net/http"
	"strconv"
)

var upgrader = websocket.Upgrader{
	ReadBufferSize:  1024,
	WriteBufferSize: 1024,
}

type WebSocketAPIObj struct {
	Data struct {
		//ServerPropertyID int    `json:"ServerPropertyId"`
		Controller string `json:"controller"`
		Method     string `json:"method"`
		CallBackID int    `json:"callBackId"`
	} `json:"data"`
}

func Initialize(path string) {
	serverSettings.Initialize(path)
	dbServices.Initialize()

	if serverSettings.WebConfig.Application.ReleaseMode == "release" {
		ginServer.Initialize(gin.ReleaseMode)
	} else {
		ginServer.Initialize(gin.DebugMode)
	}

	fileCache.Initialize()

}

func Run() {

	if serverSettings.WebConfig.Application.WebServiceOnly == false {

		loadHTMLTemplates()

		ginServer.Router.Static("/web", serverSettings.APP_LOCATION+"/web")

		ginServer.Router.GET("/ws", func(c *gin.Context) {
			webSocketHandler(c.Writer, c.Request)
		})
	}

	initializeStaticRoutes()

	go ginServer.Router.RunTLS(":"+strconv.Itoa(serverSettings.WebConfig.Application.HttpsPort), serverSettings.APP_LOCATION+"/keys/cert.pem", serverSettings.APP_LOCATION+"/keys/key.pem")

	ginServer.Router.Run(":" + strconv.Itoa(serverSettings.WebConfig.Application.HttpPort))

	fmt.Printf("GoCore Application Started")
	// go ginServer.Router.GET("/", func(c *gin.Context) {
	// 	c.Redirect(http.StatusMovedPermanently, "https://"+serverSettings.WebConfig.Application.Domain+":"+strconv.Itoa(serverSettings.WebConfig.Application.HttpsPort))
	// })

}

func webSocketHandler(w http.ResponseWriter, r *http.Request) {

	conn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Println(err)
		return
	}

	for {
		messageType, p, err := conn.ReadMessage()

		var wsAPIObj WebSocketAPIObj
		json.Unmarshal(p, &wsAPIObj)

		// Loop over structs and display them.
		// for l := range languages {
		fmt.Printf("controller = %v, method = %v", wsAPIObj.Data.Controller, wsAPIObj.Data.Method)
		fmt.Println()
		// }

		log.Println(string(p[:]))

		if err != nil {
			return
		}
		if err = conn.WriteMessage(messageType, p); err != nil {
			return
		}
	}

}

func loadHTMLTemplates() {

	if serverSettings.WebConfig.Application.HtmlTemplates.Enabled {

		levels := "/*"
		dirLevel := ""

		switch serverSettings.WebConfig.Application.HtmlTemplates.DirectoryLevels {
		case 0:
			levels = "/*"
			dirLevel = ""
		case 1:
			levels = "/**/*"
			dirLevel = "root/"
		case 2:
			levels = "/**/**/*"
			dirLevel = "root/root/"
		}

		ginServer.Router.LoadHTMLGlob(serverSettings.APP_LOCATION + "/web/" + serverSettings.WebConfig.Application.HtmlTemplates.Directory + levels)

		ginServer.Router.GET("", func(c *gin.Context) {
			c.HTML(http.StatusOK, dirLevel+"index.tmpl", gin.H{})
		})
	} else {

		if serverSettings.WebConfig.Application.DisableRootIndex {
			return
		}

		ginServer.Router.GET("", func(c *gin.Context) {
			if serverSettings.WebConfig.Application.RootIndexPath == "" {
				ginServer.ReadHTMLFile(serverSettings.APP_LOCATION+"/web/index.htm", c)
			} else {
				ginServer.ReadHTMLFile(serverSettings.APP_LOCATION+"/web/"+serverSettings.WebConfig.Application.RootIndexPath, c)
			}
		})
	}
}

func initializeStaticRoutes() {

	ginServer.Router.GET("/swagger", func(c *gin.Context) {
		// c.Redirect(http.StatusMovedPermanently, "https://"+serverSettings.WebConfig.Application.Domain+":"+strconv.Itoa(serverSettings.WebConfig.Application.HttpsPort)+"/web/swagger/dist/index.html")

		ginServer.ReadHTMLFile(serverSettings.APP_LOCATION+"/web/swagger/dist/index.html", c)
	})
}
<commit_msg>Fix for Dev.<commit_after>package app

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"strconv"

	"github.com/DanielRenne/GoCore/core/dbServices"
	"github.com/DanielRenne/GoCore/core/fileCache"
	"github.com/DanielRenne/GoCore/core/ginServer"
	"github.com/DanielRenne/GoCore/core/serverSettings"
	"github.com/gin-gonic/gin"
	"github.com/gorilla/websocket"
)

var upgrader = websocket.Upgrader{
	ReadBufferSize:  1024,
	WriteBufferSize: 1024,
}

type WebSocketAPIObj struct {
	Data struct {
		//ServerPropertyID int    `json:"ServerPropertyId"`
		Controller string `json:"controller"`
		Method     string `json:"method"`
		CallBackID int    `json:"callBackId"`
	} `json:"data"`
}

func Initialize(path string) {
	serverSettings.Initialize(path)
	dbServices.Initialize()

	if serverSettings.WebConfig.Application.ReleaseMode == "release" {
		ginServer.Initialize(gin.ReleaseMode)
	} else {
		ginServer.Initialize(gin.DebugMode)
	}

	fileCache.Initialize()

}

func Run() {

	if serverSettings.WebConfig.Application.WebServiceOnly == false {

		loadHTMLTemplates()

		ginServer.Router.Static("/web", serverSettings.APP_LOCATION+"/web")

		ginServer.Router.GET("/ws", func(c *gin.Context) {
			webSocketHandler(c.Writer, c.Request)
		})
	}

	initializeStaticRoutes()

	go ginServer.Router.RunTLS(":"+strconv.Itoa(serverSettings.WebConfig.Application.HttpsPort), serverSettings.APP_LOCATION+"/keys/cert.pem", serverSettings.APP_LOCATION+"/keys/key.pem")

	log.Println("GoCore Application Started")

	ginServer.Router.Run(":" + strconv.Itoa(serverSettings.WebConfig.Application.HttpPort))

	// go ginServer.Router.GET("/", func(c *gin.Context) {
	// 	c.Redirect(http.StatusMovedPermanently, "https://"+serverSettings.WebConfig.Application.Domain+":"+strconv.Itoa(serverSettings.WebConfig.Application.HttpsPort))
	// })

}

func webSocketHandler(w http.ResponseWriter, r *http.Request) {

	conn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Println(err)
		return
	}

	for {
		messageType, p, err := conn.ReadMessage()

		var wsAPIObj WebSocketAPIObj
		json.Unmarshal(p, &wsAPIObj)

		// Loop over structs and display them.
		// for l := range languages {
		fmt.Printf("controller = %v, method = %v", wsAPIObj.Data.Controller, wsAPIObj.Data.Method)
		fmt.Println()
		// }

		log.Println(string(p[:]))

		if err != nil {
			return
		}
		if err = conn.WriteMessage(messageType, p); err != nil {
			return
		}
	}

}

func loadHTMLTemplates() {

	if serverSettings.WebConfig.Application.HtmlTemplates.Enabled {

		levels := "/*"
		dirLevel := ""

		switch serverSettings.WebConfig.Application.HtmlTemplates.DirectoryLevels {
		case 0:
			levels = "/*"
			dirLevel = ""
		case 1:
			levels = "/**/*"
			dirLevel = "root/"
		case 2:
			levels = "/**/**/*"
			dirLevel = "root/root/"
		}

		ginServer.Router.LoadHTMLGlob(serverSettings.APP_LOCATION + "/web/" + serverSettings.WebConfig.Application.HtmlTemplates.Directory + levels)

		ginServer.Router.GET("", func(c *gin.Context) {
			c.HTML(http.StatusOK, dirLevel+"index.tmpl", gin.H{})
		})
	} else {

		if serverSettings.WebConfig.Application.DisableRootIndex {
			return
		}

		ginServer.Router.GET("", func(c *gin.Context) {
			if serverSettings.WebConfig.Application.RootIndexPath == "" {
				ginServer.ReadHTMLFile(serverSettings.APP_LOCATION+"/web/index.htm", c)
			} else {
				ginServer.ReadHTMLFile(serverSettings.APP_LOCATION+"/web/"+serverSettings.WebConfig.Application.RootIndexPath, c)
			}
		})
	}
}

func initializeStaticRoutes() {

	ginServer.Router.GET("/swagger", func(c *gin.Context) {
		// c.Redirect(http.StatusMovedPermanently, "https://"+serverSettings.WebConfig.Application.Domain+":"+strconv.Itoa(serverSettings.WebConfig.Application.HttpsPort)+"/web/swagger/dist/index.html")

		ginServer.ReadHTMLFile(serverSettings.APP_LOCATION+"/web/swagger/dist/index.html", c)
	})
}
<|endoftext|>


<commit_before>package cli

import (
	"fmt"
	"io"
	"os"

	"github.com/codegangsta/cli"
)

//go:generate ./version.go.tmpl

func Main(args []string, journal, output io.Writer) {
	App := cli.NewApp()

	App.Name = "repeatr"
	App.Usage = "Run it. Run it again."
	App.Version = "v0.13+dev"

	App.Writer = journal

	App.Commands = []cli.Command{
		RunCommandPattern(output),
		TwerkCommandPattern(os.Stdin, output, journal),
		UnpackCommandPattern(journal),
		ScanCommandPattern(output, journal),
		ExploreCommandPattern(output, journal),
		CfgCommandPattern(os.Stdin, output, journal),
	}

	// Slight touch to the phrasing on subcommands not found.
	App.CommandNotFound = func(ctx *cli.Context, command string) {
		panic(Exit.NewWith(
			fmt.Sprintf("Incorrect usage: '%s %v' is not a repeatr subcommand\n", ctx.App.Name, command),
			SetExitCode(EXIT_BADARGS),
		))
	}

	// Put some more info in our version printer.
	// Global var.  Womp womp.
	// Also, version goes to stdout.
	cli.VersionPrinter = func(ctx *cli.Context) {
		fmt.Fprintf(os.Stdout, "%v v%v\n", ctx.App.Name, ctx.App.Version)
		fmt.Fprintf(os.Stdout, "git commit %v\n", GITCOMMIT)
		fmt.Fprintf(os.Stdout, "build date %v\n", BUILDDATE)
	}

	// Invoking version as a subcommand should also fly.
	App.Commands = append(App.Commands,
		cli.Command{
			Name:  "version",
			Usage: "Shows the version of repeatr",
			Action: func(ctx *cli.Context) {
				cli.ShowVersion(ctx)
			},
		},
	)

	if err := App.Run(args); err != nil {
		panic(Exit.NewWith(
			fmt.Sprintf("Incorrect usage: %s", err),
			SetExitCode(EXIT_BADARGS),
		))
	}
}
<commit_msg>Vix vedundant v.<commit_after>package cli

import (
	"fmt"
	"io"
	"os"

	"github.com/codegangsta/cli"
)

//go:generate ./version.go.tmpl

func Main(args []string, journal, output io.Writer) {
	App := cli.NewApp()

	App.Name = "repeatr"
	App.Usage = "Run it. Run it again."
	App.Version = "v0.13+dev"

	App.Writer = journal

	App.Commands = []cli.Command{
		RunCommandPattern(output),
		TwerkCommandPattern(os.Stdin, output, journal),
		UnpackCommandPattern(journal),
		ScanCommandPattern(output, journal),
		ExploreCommandPattern(output, journal),
		CfgCommandPattern(os.Stdin, output, journal),
	}

	// Slight touch to the phrasing on subcommands not found.
	App.CommandNotFound = func(ctx *cli.Context, command string) {
		panic(Exit.NewWith(
			fmt.Sprintf("Incorrect usage: '%s %v' is not a repeatr subcommand\n", ctx.App.Name, command),
			SetExitCode(EXIT_BADARGS),
		))
	}

	// Put some more info in our version printer.
	// Global var.  Womp womp.
	// Also, version goes to stdout.
	cli.VersionPrinter = func(ctx *cli.Context) {
		fmt.Fprintf(os.Stdout, "%v %v\n", ctx.App.Name, ctx.App.Version)
		fmt.Fprintf(os.Stdout, "git commit %v\n", GITCOMMIT)
		fmt.Fprintf(os.Stdout, "build date %v\n", BUILDDATE)
	}

	// Invoking version as a subcommand should also fly.
	App.Commands = append(App.Commands,
		cli.Command{
			Name:  "version",
			Usage: "Shows the version of repeatr",
			Action: func(ctx *cli.Context) {
				cli.ShowVersion(ctx)
			},
		},
	)

	if err := App.Run(args); err != nil {
		panic(Exit.NewWith(
			fmt.Sprintf("Incorrect usage: %s", err),
			SetExitCode(EXIT_BADARGS),
		))
	}
}
<|endoftext|>


<commit_before><commit_msg>gopasta: disable autocomplete in the form<commit_after><|endoftext|>


<commit_before>package graph

import (
	"testing"

	"github.com/opentable/sous/lib"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

type resolveSourceLocationInput struct {
	Flags   *sous.ResolveFilter
	Context *sous.SourceContext
}

func TestResolveSourceLocation_failure(t *testing.T) {
	assertSourceContextError := func(t *testing.T, flags *sous.ResolveFilter, ctx *SourceContextDiscovery, msgPattern string) {
		_, actualErr := newRefinedResolveFilter(flags, ctx)
		require.NotNil(t, actualErr)
		assert.Regexp(t, msgPattern, actualErr.Error())
	}

	assertSourceContextError(t, &sous.ResolveFilter{}, &SourceContextDiscovery{},
		"no repo specified, please use -repo or run sous inside a git repo with a configured remote")
	assertSourceContextError(t, nil, &SourceContextDiscovery{},
		"no repo specified, please use -repo or run sous inside a git repo with a configured remote")
}

func assertSourceContextSuccess(t *testing.T, expected sous.ManifestID, flags *sous.ResolveFilter, ctx *sous.SourceContext) {
	disco := &SourceContextDiscovery{SourceContext: ctx}
	rrf, err := newRefinedResolveFilter(flags, disco)
	require.NoError(t, err)

	actual, err := newTargetManifestID(rrf)
	assert.NoError(t, err)
	assert.Equal(t, actual.Source.Repo, expected.Source.Repo, "repos differ")
	assert.Equal(t, actual.Source.Dir, expected.Source.Dir, "offsets differ")
	assert.Equal(t, actual.Flavor, expected.Flavor, "flavors differ")
}

func TestResolveSourceLocation_success(t *testing.T) {

	// -repo set and matches detected repo, so that repo used.
	assertSourceContextSuccess(t,
		// expected
		sous.ManifestID{
			Source: sous.SourceLocation{
				Repo: "github.com/user/project",
			},
		},
		// flags
		&sous.ResolveFilter{
			Repo: sous.NewResolveFieldMatcher("github.com/user/project"),
		},
		// context
		&sous.SourceContext{
			PrimaryRemoteURL: "github.com/user/project",
		},
	)

	// -repo and -offset set, providing full SourceLocation.
	assertSourceContextSuccess(t,
		// expected
		sous.ManifestID{
			Source: sous.SourceLocation{
				Repo: "github.com/user/project",
				Dir:  "some/path",
			},
		},
		// flags
		&sous.ResolveFilter{
			Repo:   sous.NewResolveFieldMatcher("github.com/user/project"),
			Offset: sous.NewResolveFieldMatcher("some/path"),
		},
		// context
		&sous.SourceContext{
			PrimaryRemoteURL: "github.com/user/project",
			OffsetDir:        "some/path",
		},
	)

	// -repo set explicitly, therefore detected offset ignored.
	assertSourceContextSuccess(t,
		// expected
		sous.ManifestID{
			Source: sous.SourceLocation{
				Repo: "github.com/from/flags",
			},
		},
		// flags
		&sous.ResolveFilter{
			Repo: sous.NewResolveFieldMatcher("github.com/from/flags"),
		},
		// context
		&sous.SourceContext{
			PrimaryRemoteURL: "github.com/original/context",
			OffsetDir:        "the/detected/offset",
		},
	)
}
<commit_msg>graph: TestResolveSourceLocation no flags case.<commit_after>package graph

import (
	"testing"

	"github.com/opentable/sous/lib"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

type resolveSourceLocationInput struct {
	Flags   *sous.ResolveFilter
	Context *sous.SourceContext
}

func TestResolveSourceLocation_failure(t *testing.T) {
	assertSourceContextError := func(t *testing.T, flags *sous.ResolveFilter, ctx *SourceContextDiscovery, msgPattern string) {
		_, actualErr := newRefinedResolveFilter(flags, ctx)
		require.NotNil(t, actualErr)
		assert.Regexp(t, msgPattern, actualErr.Error())
	}

	assertSourceContextError(t, &sous.ResolveFilter{}, &SourceContextDiscovery{},
		"no repo specified, please use -repo or run sous inside a git repo with a configured remote")
	assertSourceContextError(t, nil, &SourceContextDiscovery{},
		"no repo specified, please use -repo or run sous inside a git repo with a configured remote")
}

func assertSourceContextSuccess(t *testing.T, expected sous.ManifestID, flags *sous.ResolveFilter, ctx *sous.SourceContext) {
	disco := &SourceContextDiscovery{SourceContext: ctx}
	rrf, err := newRefinedResolveFilter(flags, disco)
	require.NoError(t, err)

	actual, err := newTargetManifestID(rrf)
	assert.NoError(t, err)
	assert.Equal(t, actual.Source.Repo, expected.Source.Repo, "repos differ")
	assert.Equal(t, actual.Source.Dir, expected.Source.Dir, "offsets differ")
	assert.Equal(t, actual.Flavor, expected.Flavor, "flavors differ")
}

func TestResolveSourceLocation_success(t *testing.T) {

	// -repo set and matches detected repo, so that repo used.
	assertSourceContextSuccess(t,
		// expected
		sous.ManifestID{
			Source: sous.SourceLocation{
				Repo: "github.com/user/project",
			},
		},
		// flags
		&sous.ResolveFilter{
			Repo: sous.NewResolveFieldMatcher("github.com/user/project"),
		},
		// context
		&sous.SourceContext{
			PrimaryRemoteURL: "github.com/user/project",
		},
	)

	// -repo and -offset set, providing full SourceLocation.
	assertSourceContextSuccess(t,
		// expected
		sous.ManifestID{
			Source: sous.SourceLocation{
				Repo: "github.com/user/project",
				Dir:  "some/path",
			},
		},
		// flags
		&sous.ResolveFilter{
			Repo:   sous.NewResolveFieldMatcher("github.com/user/project"),
			Offset: sous.NewResolveFieldMatcher("some/path"),
		},
		// context
		&sous.SourceContext{
			PrimaryRemoteURL: "github.com/user/project",
			OffsetDir:        "some/path",
		},
	)

	// -repo set explicitly, therefore detected offset ignored.
	assertSourceContextSuccess(t,
		// expected
		sous.ManifestID{
			Source: sous.SourceLocation{
				Repo: "github.com/from/flags",
			},
		},
		// flags
		&sous.ResolveFilter{
			Repo: sous.NewResolveFieldMatcher("github.com/from/flags"),
		},
		// context
		&sous.SourceContext{
			PrimaryRemoteURL: "github.com/original/context",
			OffsetDir:        "the/detected/offset",
		},
	)

	// No flags, therefore use detected repo and offset.
	assertSourceContextSuccess(t,
		// expected
		sous.ManifestID{
			Source: sous.SourceLocation{
				Repo: "github.com/original/context",
				Dir:  "the/detected/offset",
			},
		},
		// flags (empty)
		&sous.ResolveFilter{},
		// context
		&sous.SourceContext{
			PrimaryRemoteURL: "github.com/original/context",
			OffsetDir:        "the/detected/offset",
		},
	)
}
<|endoftext|>