<commit_before>package jgl

import (
	"encoding/json"
	"github.com/Stymphalian/go.math/lmath"
	"image/color"
	"math"
)

// A stupid wraper around lmath.Vec4
// Most of the functions just pass it off to the underlying implementation
// to do all the grunt work.
type color4 lmath.Vec4

func (this color4) ToRGBA() color.RGBA {
	return color.RGBA{
		uint8(this.X * 255),
		uint8(this.Y * 255),
		uint8(this.Z * 255),
		uint8(this.W * 255),
	}
}

func (this color4) Add(other color4) color4 {
	return color4(lmath.Vec4(this).Add(lmath.Vec4(other)))
}

func (this color4) Sub(other color4) color4 {
	return color4(lmath.Vec4(this).Sub(lmath.Vec4(other)))
}

func (this color4) Outer(other color4) color4 {
	return color4(lmath.Vec4(this).Outer(lmath.Vec4(other)))
}

func (this color4) Map(f func(x float64) float64) (out color4) {
	out.X = f(this.X)
	out.Y = f(this.Y)
	out.Z = f(this.Z)
	out.W = f(this.W)
	return
}

func (this color4) Clamp(min, max float64) color4 {
	this.X = lmath.Clamp(this.X, min, max)
	this.Y = lmath.Clamp(this.Y, min, max)
	this.Z = lmath.Clamp(this.Z, min, max)
	this.W = lmath.Clamp(this.W, min, max)
	return this
}

func (this color4) Min(min float64) color4 {
	this.X = math.Min(this.X, min)
	this.Y = math.Min(this.Y, min)
	this.Z = math.Min(this.Z, min)
	this.W = math.Min(this.W, min)
	return this
}

func (this color4) Max(max float64) color4 {
	this.X = math.Max(this.X, max)
	this.Y = math.Max(this.Y, max)
	this.Z = math.Max(this.Z, max)
	this.W = math.Max(this.W, max)
	return this
}

func (this color4) MarshalJSON() ([]byte, error) {
	data := [4]float64{this.X, this.Y, this.Z, this.W}
	return json.Marshal(data)
}

func (this *color4) UnmarshalJSON(data []byte) (err error) {
	var d [4]float64
	err = json.Unmarshal(data, &d)
	if err != nil {
		return
	}

	this.X, this.Y, this.Z, this.W = d[0], d[1], d[2], d[3]
	return nil
}
<commit_msg>convert to color4 is-a lmath.Vec4<commit_after>package jgl

import (
	"encoding/json"
	"github.com/Stymphalian/go.math/lmath"
	"image/color"
	"math"
)

type color4 struct {
	lmath.Vec4 // unnamed field implies an "is-a" relationship
}

func (this color4) ToRGBA() color.RGBA {
	return color.RGBA{
		uint8(this.X * 255),
		uint8(this.Y * 255),
		uint8(this.Z * 255),
		uint8(this.W * 255),
	}
}

func (this color4) Map(f func(x float64) float64) (out color4) {
	out.X = f(this.X)
	out.Y = f(this.Y)
	out.Z = f(this.Z)
	out.W = f(this.W)
	return
}

func (this color4) Clamp(min, max float64) color4 {
	this.X = lmath.Clamp(this.X, min, max)
	this.Y = lmath.Clamp(this.Y, min, max)
	this.Z = lmath.Clamp(this.Z, min, max)
	this.W = lmath.Clamp(this.W, min, max)
	return this
}

func (this color4) Min(min float64) color4 {
	this.X = math.Min(this.X, min)
	this.Y = math.Min(this.Y, min)
	this.Z = math.Min(this.Z, min)
	this.W = math.Min(this.W, min)
	return this
}

func (this color4) Max(max float64) color4 {
	this.X = math.Max(this.X, max)
	this.Y = math.Max(this.Y, max)
	this.Z = math.Max(this.Z, max)
	this.W = math.Max(this.W, max)
	return this
}

func (this color4) MarshalJSON() ([]byte, error) {
	data := [4]float64{this.X, this.Y, this.Z, this.W}
	return json.Marshal(data)
}

func (this *color4) UnmarshalJSON(data []byte) (err error) {
	var d [4]float64
	err = json.Unmarshal(data, &d)
	if err != nil {
		return
	}

	this.X, this.Y, this.Z, this.W = d[0], d[1], d[2], d[3]
	return nil
}
<|endoftext|>


<commit_before>package user

import (
	"net/http"
	"xorkevin.dev/governor"
)

type (
	resSession struct {
		SessionID string `json:"session_id"`
		Userid    string `json:"userid"`
		Time      int64  `json:"time"`
		IPAddr    string `json:"ip"`
		UserAgent string `json:"user_agent"`
	}

	resUserGetSessions struct {
		Sessions []resSession `json:"active_sessions"`
	}
)

func (s *service) GetUserSessions(userid string) (*resUserGetSessions, error) {
	m, err := s.sessions.GetUserSessions(userid, 256, 0)
	if err != nil {
		return nil, governor.NewError("Failed to get user sessions", http.StatusInternalServerError, err)
	}
	res := make([]resSession, 0, len(m))
	for _, i := range m {
		res = append(res, resSession{
			SessionID: i.SessionID,
			Userid:    i.Userid,
			Time:      i.Time,
			IPAddr:    i.IPAddr,
			UserAgent: i.UserAgent,
		})
	}
	return &resUserGetSessions{
		Sessions: res,
	}, nil
}

// KillCacheSessions terminates user sessions in cache
func (s *service) KillCacheSessions(sessionids []string) error {
	if err := s.kvsessions.Del(sessionids...); err != nil {
		return governor.NewError("Failed to delete session keys", http.StatusInternalServerError, err)
	}
	return nil
}

// KillSessions terminates user sessions
func (s *service) KillSessions(sessionids []string) error {
	if err := s.KillCacheSessions(sessionids); err != nil {
		return err
	}
	if err := s.sessions.DeleteSessions(sessionids); err != nil {
		return governor.NewError("Failed to delete user sessions", http.StatusInternalServerError, err)
	}
	return nil
}

// KillAllCacheSessions terminates all sessions of a user in cache
func (s *service) KillAllCacheSessions(userid string) error {
	sessionids, err := s.sessions.GetUserSessionIDs(userid, 65536, 0)
	if err != nil {
		return governor.NewError("Failed to get user session ids", http.StatusInternalServerError, err)
	}
	if err := s.KillCacheSessions(sessionids); err != nil {
		return err
	}
	return nil
}

// KillAllSessions terminates all sessions of a user
func (s *service) KillAllSessions(userid string) error {
	if err := s.KillAllCacheSessions(userid); err != nil {
		return err
	}
	if err := s.sessions.DeleteUserSessions(userid); err != nil {
		return governor.NewError("Failed to delete user sessions", http.StatusInternalServerError, err)
	}
	return nil
}
<commit_msg>Bugfix empty redis delete<commit_after>package user

import (
	"net/http"
	"xorkevin.dev/governor"
)

type (
	resSession struct {
		SessionID string `json:"session_id"`
		Userid    string `json:"userid"`
		Time      int64  `json:"time"`
		IPAddr    string `json:"ip"`
		UserAgent string `json:"user_agent"`
	}

	resUserGetSessions struct {
		Sessions []resSession `json:"active_sessions"`
	}
)

func (s *service) GetUserSessions(userid string) (*resUserGetSessions, error) {
	m, err := s.sessions.GetUserSessions(userid, 256, 0)
	if err != nil {
		return nil, governor.NewError("Failed to get user sessions", http.StatusInternalServerError, err)
	}
	res := make([]resSession, 0, len(m))
	for _, i := range m {
		res = append(res, resSession{
			SessionID: i.SessionID,
			Userid:    i.Userid,
			Time:      i.Time,
			IPAddr:    i.IPAddr,
			UserAgent: i.UserAgent,
		})
	}
	return &resUserGetSessions{
		Sessions: res,
	}, nil
}

// KillCacheSessions terminates user sessions in cache
func (s *service) KillCacheSessions(sessionids []string) error {
	if len(sessionids) == 0 {
		return nil
	}

	if err := s.kvsessions.Del(sessionids...); err != nil {
		return governor.NewError("Failed to delete session keys", http.StatusInternalServerError, err)
	}
	return nil
}

// KillSessions terminates user sessions
func (s *service) KillSessions(sessionids []string) error {
	if len(sessionids) == 0 {
		return nil
	}

	if err := s.KillCacheSessions(sessionids); err != nil {
		return err
	}
	if err := s.sessions.DeleteSessions(sessionids); err != nil {
		return governor.NewError("Failed to delete user sessions", http.StatusInternalServerError, err)
	}
	return nil
}

// KillAllCacheSessions terminates all sessions of a user in cache
func (s *service) KillAllCacheSessions(userid string) error {
	sessionids, err := s.sessions.GetUserSessionIDs(userid, 65536, 0)
	if err != nil {
		return governor.NewError("Failed to get user session ids", http.StatusInternalServerError, err)
	}
	if err := s.KillCacheSessions(sessionids); err != nil {
		return err
	}
	return nil
}

// KillAllSessions terminates all sessions of a user
func (s *service) KillAllSessions(userid string) error {
	if err := s.KillAllCacheSessions(userid); err != nil {
		return err
	}
	if err := s.sessions.DeleteUserSessions(userid); err != nil {
		return governor.NewError("Failed to delete user sessions", http.StatusInternalServerError, err)
	}
	return nil
}
<|endoftext|>


<commit_before>package es

import (
	"log"
	"os"
	"time"
)

var timer = time.NewTimer(1 * time.Second)

type IndexerStats struct {
	Runs        int64
	IndexedDocs int64
	TotalTime   time.Duration
}

func (stats *IndexerStats) Add(count int, dur time.Duration) {
	stats.Runs++
	stats.IndexedDocs += int64(count)
	stats.TotalTime += dur
}

type Indexer struct {
	Index *Index

	IndexEvery time.Duration // triggers a new index run after that duration, will be reset when BatchSize reached
	BatchSize  int           // triggers a new index run when the batch reaches that size

	docsBatch   []*Doc
	docsChannel chan *Doc
	timer       *time.Timer
	Stats       IndexerStats
}

func (indexer *Indexer) Finish() error {
	return indexer.indexBatch()
}

func (indexer *Indexer) resetBatch() {
	indexer.docsBatch = make([]*Doc, 0, indexer.BatchSize)
}

func (indexer *Indexer) resetTimer() bool {
	return indexer.timer.Reset(indexer.IndexEvery)
}

var logger = log.New(os.Stderr, "", 0)

func (indexer *Indexer) Start() chan *Doc {
	if indexer.BatchSize == 0 {
		indexer.BatchSize = 100
	}
	if indexer.IndexEvery == 0 {
		indexer.IndexEvery = 1 * time.Hour
	}
	indexer.timer = time.NewTimer(indexer.IndexEvery)
	indexer.docsChannel = make(chan *Doc, indexer.BatchSize)
	indexer.resetBatch()
	go func(indexer *Indexer) {
		for {
			select {
			case <-indexer.timer.C:
				e := indexer.indexBatch()
				if e != nil {
					logger.Printf("ERROR=%q", e)
				}
				indexer.resetTimer()
			case doc, ok := <-indexer.docsChannel:
				if !ok {
					indexer.timer.Stop()
					e := indexer.indexBatch()
					if e != nil {
						logger.Printf("ERROR=%q", e)
					}
					return
				}
				if doc.Index == "" {
					doc.Index = indexer.Index.Index
				}
				if doc.Type == "" {
					doc.Type = indexer.Index.Type
				}
				indexer.docsBatch = append(indexer.docsBatch, doc)
				if len(indexer.docsBatch) >= indexer.BatchSize {
					indexer.timer.Stop()
					indexer.indexBatch()
					indexer.resetTimer()
				}
			}
		}
	}(indexer)
	return indexer.docsChannel
}

func (indexer *Indexer) indexBatch() error {
	if len(indexer.docsBatch) < 1 {
		return nil
	}
	started := time.Now()
	e := indexer.Index.IndexDocs(indexer.docsBatch)
	if e != nil {
		return e
	}
	indexer.Stats.Add(len(indexer.docsBatch), time.Now().Sub(started))
	indexer.resetBatch()
	return nil
}
<commit_msg>fix closing of indexer (implements io.Closer() now)<commit_after>package es

import (
	"log"
	"os"
	"time"
)

var timer = time.NewTimer(1 * time.Second)

type IndexerStats struct {
	Runs        int64
	IndexedDocs int64
	TotalTime   time.Duration
}

func (stats *IndexerStats) Add(count int, dur time.Duration) {
	stats.Runs++
	stats.IndexedDocs += int64(count)
	stats.TotalTime += dur
}

type Indexer struct {
	Index *Index

	IndexEvery time.Duration // triggers a new index run after that duration, will be reset when BatchSize reached
	BatchSize  int           // triggers a new index run when the batch reaches that size

	docsBatch   []*Doc
	docsChannel chan *Doc
	timer       *time.Timer
	Stats       IndexerStats
}

func (indexer *Indexer) Finish() error {
	return indexer.indexBatch()
}

func (indexer *Indexer) resetBatch() {
	indexer.docsBatch = make([]*Doc, 0, indexer.BatchSize)
}

func (indexer *Indexer) resetTimer() bool {
	return indexer.timer.Reset(indexer.IndexEvery)
}

var logger = log.New(os.Stderr, "", 0)

func (indexer *Indexer) Start() chan *Doc {
	if indexer.BatchSize == 0 {
		indexer.BatchSize = 100
	}
	if indexer.IndexEvery == 0 {
		indexer.IndexEvery = 1 * time.Hour
	}
	indexer.timer = time.NewTimer(indexer.IndexEvery)
	indexer.docsChannel = make(chan *Doc, indexer.BatchSize)
	indexer.resetBatch()
	go func(indexer *Indexer) {
		for {
			select {
			case <-indexer.timer.C:
				e := indexer.indexBatch()
				if e != nil {
					logger.Printf("ERROR=%q", e)
				}
				indexer.resetTimer()
			case doc, ok := <-indexer.docsChannel:
				if !ok {
					indexer.timer.Stop()
					e := indexer.indexBatch()
					if e != nil {
						logger.Printf("ERROR=%q", e)
					}
					return
				}
				if doc.Index == "" {
					doc.Index = indexer.Index.Index
				}
				if doc.Type == "" {
					doc.Type = indexer.Index.Type
				}
				indexer.docsBatch = append(indexer.docsBatch, doc)
				if len(indexer.docsBatch) >= indexer.BatchSize {
					indexer.timer.Stop()
					indexer.indexBatch()
					indexer.resetTimer()
				}
			}
		}
	}(indexer)
	return indexer.docsChannel
}

func (indexer *Indexer) Close() error {
	close(indexer.docsChannel)
	return indexer.indexBatch()
}

func (indexer *Indexer) indexBatch() error {
	if len(indexer.docsBatch) < 1 {
		return nil
	}
	started := time.Now()
	e := indexer.Index.IndexDocs(indexer.docsBatch)
	if e != nil {
		return e
	}
	indexer.Stats.Add(len(indexer.docsBatch), time.Now().Sub(started))
	indexer.resetBatch()
	return nil
}
<|endoftext|>


<commit_before>package firebirdsql

import (
	"database/sql"
	"math/rand"
	"net"
	"sync"
	"testing"
	"time"
)

func TestEventsCallback(t *testing.T) {
	tempPathDB := TempFileName("test_events_")
	conn, err := sql.Open("firebirdsql_createdb", "sysdba:masterkey@localhost:3050"+tempPathDB)
	if err != nil {
		t.Fatalf("Error connecting: %v", err)
	}
	conn.Ping()
	conn.Close()

	fbevent, err := NewFBEvent("sysdba:masterkey@localhost:3050" + tempPathDB)
	if err != nil {
		t.Fatalf("Error connecting: %v", err)
	}
	defer fbevent.Close()

	doEvent := func(wg *sync.WaitGroup, wantEvents map[string]int) {
		eventsSlice := make([]string, 0, len(wantEvents))
		events := make(map[string]int, len(wantEvents))
		for event, count := range wantEvents {
			if count > 0 {
				eventsSlice = append(eventsSlice, event)
				events[event] = count
			}
		}
		waitDone := len(events)
		for len(events) > 0 {
			idx := rand.Intn(len(events))
			name := eventsSlice[idx]

			if err := fbevent.PostEvent(name); err != nil {
				for i := 0; i < waitDone; i++ {
					wg.Done()
				}
				t.Error(err)
				return
			}
			time.Sleep(time.Millisecond * 5)
			events[name]--
			if events[name] <= 0 {
				delete(events, name)
				eventsSlice = append(eventsSlice[:idx], eventsSlice[idx+1:]...)
				wg.Done()
				waitDone--
			}
		}

	}

	t.Run("callback", func(t *testing.T) {
		wg := &sync.WaitGroup{}

		wantEvents := map[string]int{
			"event_1": 12,
			"event_2": 15,
			"event_3": 23,
			"event_4": 0,
		}
		events := make([]string, 0, len(wantEvents))
		for event := range wantEvents {
			events = append(events, event)
		}
		wg.Add(3)

		muEvents := &sync.Mutex{}
		gotEvents := map[string]int{}

		subscribe, err := fbevent.Subscribe(events, func(e Event) {
			muEvents.Lock()
			gotEvents[e.Name] += e.Count
			muEvents.Unlock()
		})
		if err != nil {
			t.Error(err)
		}
		defer subscribe.Unsubscribe()

		go doEvent(wg, wantEvents)
		wg.Wait()
		time.Sleep(time.Second * 1)

		muEvents.Lock()
		for wantEvent, wantCount := range wantEvents {
			if wantCount <= 0 {
				continue
			}
			gotCount, ok := gotEvents[wantEvent]
			if !ok {
				t.Errorf("Expected %s count %d", wantEvent, wantCount)
			} else if gotCount != wantCount {
				t.Errorf("Expected %s count %d, got %d", wantEvent, wantCount, gotCount)
			}
		}
		muEvents.Unlock()
	})

	t.Run("channel", func(t *testing.T) {
		wg := &sync.WaitGroup{}

		wantEvents := map[string]int{
			"event_ch_1": 31,
			"event_ch_2": 21,
			"event_ch_3": 15,
			"event_ch_4": 0,
		}
		events := make([]string, 0, len(wantEvents))
		for event := range wantEvents {
			events = append(events, event)
		}
		wg.Add(3)

		muEvents := &sync.Mutex{}
		gotEvents := map[string]int{}
		chEvent := make(chan Event)
		subscribe, err := fbevent.SubscribeChan(events, chEvent)
		if err != nil {
			t.Error(err)

		}
		chClose := make(chan error)
		subscribe.NotifyClose(chClose)
		go func() {
			for {
				select {
				case e := <-chEvent:
					muEvents.Lock()
					gotEvents[e.Name] += e.Count
					muEvents.Unlock()

				case err := <-chClose:
					if err != nil {
						if _, ok := err.(*net.OpError); !ok {
							t.Error(err)
						}
					}
					return
				}
			}
		}()
		defer subscribe.Unsubscribe()

		go doEvent(wg, wantEvents)
		wg.Wait()
		time.Sleep(time.Second * 1)

		muEvents.Lock()
		for wantEvent, wantCount := range wantEvents {
			if wantCount <= 0 {
				continue
			}
			gotCount, ok := gotEvents[wantEvent]
			if !ok {
				t.Errorf("Expected %s count %d", wantEvent, wantCount)
			} else if gotCount != wantCount {
				t.Errorf("Expected %s count %d, got %d", wantEvent, wantCount, gotCount)
			}

		}
		muEvents.Unlock()
	})
}
<commit_msg>event: fixed test; subscribe test<commit_after>package firebirdsql

import (
	"database/sql"
	"math/rand"
	"net"
	"sync"
	"testing"
	"time"
)

func TestEventsCallback(t *testing.T) {
	tempPathDB := TempFileName("test_events_")
	dsn := "sysdba:masterkey@localhost:3050" + tempPathDB
	conn, err := sql.Open("firebirdsql_createdb", dsn)
	if err != nil {
		t.Fatalf("Error connecting: %v", err)
	}
	conn.Ping()
	conn.Close()

	fbevent, err := NewFBEvent(dsn)
	if err != nil {
		t.Fatalf("Error connecting: %v", err)
	}
	defer fbevent.Close()

	doEvent := func(wg *sync.WaitGroup, wantEvents map[string]int) {
		eventsSlice := make([]string, 0, len(wantEvents))
		events := make(map[string]int, len(wantEvents))
		for event, count := range wantEvents {
			if count > 0 {
				eventsSlice = append(eventsSlice, event)
				events[event] = count
			}
		}
		waitDone := len(events)
		for len(events) > 0 {
			idx := rand.Intn(len(events))
			name := eventsSlice[idx]

			if err := fbevent.PostEvent(name); err != nil {
				for i := 0; i < waitDone; i++ {
					wg.Done()
				}
				t.Error(err)
				return
			}
			events[name]--
			if events[name] <= 0 {
				delete(events, name)
				eventsSlice = append(eventsSlice[:idx], eventsSlice[idx+1:]...)
				wg.Done()
				waitDone--
			}
		}

	}

	t.Run("callback", func(t *testing.T) {
		wg := &sync.WaitGroup{}

		wantEvents := map[string]int{
			"event_1": 12,
			"event_2": 15,
			"event_3": 23,
		}
		events := make([]string, 0, len(wantEvents))
		for event := range wantEvents {
			events = append(events, event)
		}
		wg.Add(3)

		muEvents := &sync.Mutex{}
		gotEvents := map[string]int{}

		subscribe, err := fbevent.Subscribe(events, func(e Event) {
			muEvents.Lock()
			gotEvents[e.Name] += e.Count
			muEvents.Unlock()
			time.Sleep(time.Millisecond * time.Duration(rand.Intn(10)))
		})
		if err != nil {
			t.Fatal(err)
		}
		defer subscribe.Unsubscribe()

		go doEvent(wg, wantEvents)
		wg.Wait()
		time.Sleep(time.Second * 1)

		muEvents.Lock()
		for wantEvent, wantCount := range wantEvents {
			if wantCount <= 0 {
				continue
			}
			gotCount, ok := gotEvents[wantEvent]
			if !ok {
				t.Errorf("Expected %s count %d", wantEvent, wantCount)
			} else if gotCount != wantCount {
				t.Errorf("Expected %s count %d, got %d", wantEvent, wantCount, gotCount)
			}
		}
		muEvents.Unlock()
	})

	t.Run("channel", func(t *testing.T) {
		wg := &sync.WaitGroup{}

		wantEvents := map[string]int{
			"event_ch_1": 31,
			"event_ch_2": 21,
			"event_ch_3": 15,
		}
		events := make([]string, 0, len(wantEvents))
		for event := range wantEvents {
			events = append(events, event)
		}
		wg.Add(3)

		muEvents := &sync.Mutex{}
		gotEvents := map[string]int{}
		chEvent := make(chan Event)
		subscribe, err := fbevent.SubscribeChan(events, chEvent)
		if err != nil {
			t.Fatal(err)
		}
		chClose := make(chan error)
		subscribe.NotifyClose(chClose)
		go func() {
			for {
				select {
				case e := <-chEvent:
					muEvents.Lock()
					gotEvents[e.Name] += e.Count
					muEvents.Unlock()
					time.Sleep(time.Millisecond * time.Duration(rand.Intn(10)))
				case err := <-chClose:
					if err != nil {
						if _, ok := err.(*net.OpError); !ok {
							t.Error(err)
						}
					}
					return
				}
			}
		}()
		defer subscribe.Unsubscribe()

		go doEvent(wg, wantEvents)
		wg.Wait()
		time.Sleep(time.Second * 1)

		muEvents.Lock()
		for wantEvent, wantCount := range wantEvents {
			if wantCount <= 0 {
				continue
			}
			gotCount, ok := gotEvents[wantEvent]
			if !ok {
				t.Errorf("Expected %s count %d", wantEvent, wantCount)
			} else if gotCount != wantCount {
				t.Errorf("Expected %s count %d, got %d", wantEvent, wantCount, gotCount)
			}

		}
		muEvents.Unlock()
	})
}

func TestSubscribe(t *testing.T) {
	tempPathDB := TempFileName("test_subscribe_")
	dsn := "sysdba:masterkey@localhost:3050" + tempPathDB
	conn, err := sql.Open("firebirdsql_createdb", dsn)
	if err != nil {
		t.Fatalf("Error connecting: %v", err)
	}
	conn.Ping()
	conn.Close()

	fbevent, err := NewFBEvent(dsn)
	if err != nil {
		t.Fatalf("Error connecting: %v", err)
	}
	defer fbevent.Close()
	events := []string{"event1", "event2"}
	subscriber1, err := fbevent.Subscribe(events, func(Event) {
	})
	if err != nil {
		t.Fatal(err)
	}
	subscriber2, err := fbevent.Subscribe(events, func(Event) {
	})
	if err != nil {
		t.Fatal(err)
	}

	if l := len(fbevent.Subscribers()); l != 2 {
		t.Errorf("expected len subscribers %d, got %d", 2, l)
	}

	subscriber2.Unsubscribe()
	time.Sleep(time.Millisecond * 50)

	if l := len(fbevent.Subscribers()); l != 1 {
		t.Errorf("expected len subscribers %d, got %d", 1, l)
	}
	if fbevent.Subscribers()[0] != subscriber1 {
		t.Errorf("expected subscriber1")
	}

	fbevent.Close()
	if l := len(fbevent.Subscribers()); l != 0 {
		t.Errorf("unexpected subscribers %d", l)
	}

	if !subscriber2.IsClose() {
		t.Errorf("expected closed subscriber")
	}
}
<|endoftext|>


<commit_before>//
// Written by Maxim Khitrov (November 2012)
//

package flowrate

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

const (
	_50ms  = 50 * time.Millisecond
	_100ms = 100 * time.Millisecond
	_200ms = 200 * time.Millisecond
	_300ms = 300 * time.Millisecond
	_400ms = 400 * time.Millisecond
	_500ms = 500 * time.Millisecond
)

func nextStatus(m *Monitor) Status {
	samples := m.samples
	for i := 0; i < 30; i++ {
		if s := m.Status(); s.Samples != samples {
			return s
		}
		time.Sleep(5 * time.Millisecond)
	}
	return m.Status()
}

func TestReader(t *testing.T) {
	in := make([]byte, 100)
	for i := range in {
		in[i] = byte(i)
	}
	b := make([]byte, 100)
	r := NewReader(bytes.NewReader(in), 100)
	start := time.Now()

	// Make sure r implements Limiter
	_ = Limiter(r)

	// 1st read of 10 bytes is performed immediately
	if n, err := r.Read(b); n != 10 || err != nil {
		t.Fatalf("r.Read(b) expected 10 (<nil>); got %v (%v)", n, err)
	} else if rt := time.Since(start); rt > _50ms {
		t.Fatalf("r.Read(b) took too long (%v)", rt)
	}

	// No new Reads allowed in the current sample
	r.SetBlocking(false)
	if n, err := r.Read(b); n != 0 || err != nil {
		t.Fatalf("r.Read(b) expected 0 (<nil>); got %v (%v)", n, err)
	} else if rt := time.Since(start); rt > _50ms {
		t.Fatalf("r.Read(b) took too long (%v)", rt)
	}

	status := [6]Status{0: r.Status()} // No samples in the first status

	// 2nd read of 10 bytes blocks until the next sample
	r.SetBlocking(true)
	if n, err := r.Read(b[10:]); n != 10 || err != nil {
		t.Fatalf("r.Read(b[10:]) expected 10 (<nil>); got %v (%v)", n, err)
	} else if rt := time.Since(start); rt < _100ms {
		t.Fatalf("r.Read(b[10:]) returned ahead of time (%v)", rt)
	}

	status[1] = r.Status()            // 1st sample
	status[2] = nextStatus(r.Monitor) // 2nd sample
	status[3] = nextStatus(r.Monitor) // No activity for the 3rd sample

	if n := r.Done(); n != 20 {
		t.Fatalf("r.Done() expected 20; got %v", n)
	}

	status[4] = r.Status()
	status[5] = nextStatus(r.Monitor) // Timeout
	start = status[0].Start

	// Active, Start, Duration, Idle, Bytes, Samples, InstRate, CurRate, AvgRate, PeakRate, BytesRem, TimeRem, Progress
	want := []Status{
		Status{true, start, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		Status{true, start, _100ms, 0, 10, 1, 100, 100, 100, 100, 0, 0, 0},
		Status{true, start, _200ms, _100ms, 20, 2, 100, 100, 100, 100, 0, 0, 0},
		Status{true, start, _300ms, _200ms, 20, 3, 0, 90, 67, 100, 0, 0, 0},
		Status{false, start, _300ms, 0, 20, 3, 0, 0, 67, 100, 0, 0, 0},
		Status{false, start, _300ms, 0, 20, 3, 0, 0, 67, 100, 0, 0, 0},
	}
	for i, s := range status {
		if !reflect.DeepEqual(&s, &want[i]) {
			t.Errorf("r.Status(%v)\nexpected: %v\ngot     : %v", i, want[i], s)
		}
	}
	if !bytes.Equal(b[:20], in[:20]) {
		t.Errorf("r.Read() input doesn't match output")
	}
}

func TestWriter(t *testing.T) {
	b := make([]byte, 100)
	for i := range b {
		b[i] = byte(i)
	}
	w := NewWriter(&bytes.Buffer{}, 200)
	start := time.Now()

	// Make sure w implements Limiter
	_ = Limiter(w)

	// Non-blocking 20-byte write for the first sample returns ErrLimit
	w.SetBlocking(false)
	if n, err := w.Write(b); n != 20 || err != ErrLimit {
		t.Fatalf("w.Write(b) expected 20 (ErrLimit); got %v (%v)", n, err)
	} else if rt := time.Since(start); rt > _50ms {
		t.Fatalf("w.Write(b) took too long (%v)", rt)
	}

	// Blocking 80-byte write
	w.SetBlocking(true)
	if n, err := w.Write(b[20:]); n != 80 || err != nil {
		t.Fatalf("w.Write(b[20:]) expected 80 (<nil>); got %v (%v)", n, err)
	} else if rt := time.Since(start); rt < _400ms {
		t.Fatalf("w.Write(b[20:]) returned ahead of time (%v)", rt)
	}

	w.SetTransferSize(100)
	status := []Status{w.Status(), nextStatus(w.Monitor)}
	start = status[0].Start

	// Active, Start, Duration, Idle, Bytes, Samples, InstRate, CurRate, AvgRate, PeakRate, BytesRem, TimeRem, Progress
	want := []Status{
		Status{true, start, _400ms, 0, 80, 4, 200, 200, 200, 200, 20, _100ms, 80000},
		Status{true, start, _500ms, _100ms, 100, 5, 200, 200, 200, 200, 0, 0, 100000},
	}
	for i, s := range status {
		if !reflect.DeepEqual(&s, &want[i]) {
			t.Errorf("w.Status(%v)\nexpected: %v\ngot     : %v\n", i, want[i], s)
		}
	}
	if !bytes.Equal(b, w.Writer.(*bytes.Buffer).Bytes()) {
		t.Errorf("w.Write() input doesn't match output")
	}
}
<commit_msg>don't do DeepEqual, compare ranges for durations and rates (Refs #16)<commit_after>//
// Written by Maxim Khitrov (November 2012)
//

package flowrate

import (
	"bytes"
	"testing"
	"time"
)

const (
	_50ms  = 50 * time.Millisecond
	_100ms = 100 * time.Millisecond
	_200ms = 200 * time.Millisecond
	_300ms = 300 * time.Millisecond
	_400ms = 400 * time.Millisecond
	_500ms = 500 * time.Millisecond
)

func nextStatus(m *Monitor) Status {
	samples := m.samples
	for i := 0; i < 30; i++ {
		if s := m.Status(); s.Samples != samples {
			return s
		}
		time.Sleep(5 * time.Millisecond)
	}
	return m.Status()
}

func TestReader(t *testing.T) {
	in := make([]byte, 100)
	for i := range in {
		in[i] = byte(i)
	}
	b := make([]byte, 100)
	r := NewReader(bytes.NewReader(in), 100)
	start := time.Now()

	// Make sure r implements Limiter
	_ = Limiter(r)

	// 1st read of 10 bytes is performed immediately
	if n, err := r.Read(b); n != 10 || err != nil {
		t.Fatalf("r.Read(b) expected 10 (<nil>); got %v (%v)", n, err)
	} else if rt := time.Since(start); rt > _50ms {
		t.Fatalf("r.Read(b) took too long (%v)", rt)
	}

	// No new Reads allowed in the current sample
	r.SetBlocking(false)
	if n, err := r.Read(b); n != 0 || err != nil {
		t.Fatalf("r.Read(b) expected 0 (<nil>); got %v (%v)", n, err)
	} else if rt := time.Since(start); rt > _50ms {
		t.Fatalf("r.Read(b) took too long (%v)", rt)
	}

	status := [6]Status{0: r.Status()} // No samples in the first status

	// 2nd read of 10 bytes blocks until the next sample
	r.SetBlocking(true)
	if n, err := r.Read(b[10:]); n != 10 || err != nil {
		t.Fatalf("r.Read(b[10:]) expected 10 (<nil>); got %v (%v)", n, err)
	} else if rt := time.Since(start); rt < _100ms {
		t.Fatalf("r.Read(b[10:]) returned ahead of time (%v)", rt)
	}

	status[1] = r.Status()            // 1st sample
	status[2] = nextStatus(r.Monitor) // 2nd sample
	status[3] = nextStatus(r.Monitor) // No activity for the 3rd sample

	if n := r.Done(); n != 20 {
		t.Fatalf("r.Done() expected 20; got %v", n)
	}

	status[4] = r.Status()
	status[5] = nextStatus(r.Monitor) // Timeout
	start = status[0].Start

	// Active, Start, Duration, Idle, Bytes, Samples, InstRate, CurRate, AvgRate, PeakRate, BytesRem, TimeRem, Progress
	want := []Status{
		Status{true, start, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		Status{true, start, _100ms, 0, 10, 1, 100, 100, 100, 100, 0, 0, 0},
		Status{true, start, _200ms, _100ms, 20, 2, 100, 100, 100, 100, 0, 0, 0},
		Status{true, start, _300ms, _200ms, 20, 3, 0, 90, 67, 100, 0, 0, 0},
		Status{false, start, _300ms, 0, 20, 3, 0, 0, 67, 100, 0, 0, 0},
		Status{false, start, _300ms, 0, 20, 3, 0, 0, 67, 100, 0, 0, 0},
	}
	for i, s := range status {
		if !statusesAreEqual(&s, &want[i]) {
			t.Errorf("r.Status(%v)\nexpected: %v\ngot     : %v", i, want[i], s)
		}
	}
	if !bytes.Equal(b[:20], in[:20]) {
		t.Errorf("r.Read() input doesn't match output")
	}
}

func TestWriter(t *testing.T) {
	b := make([]byte, 100)
	for i := range b {
		b[i] = byte(i)
	}
	w := NewWriter(&bytes.Buffer{}, 200)
	start := time.Now()

	// Make sure w implements Limiter
	_ = Limiter(w)

	// Non-blocking 20-byte write for the first sample returns ErrLimit
	w.SetBlocking(false)
	if n, err := w.Write(b); n != 20 || err != ErrLimit {
		t.Fatalf("w.Write(b) expected 20 (ErrLimit); got %v (%v)", n, err)
	} else if rt := time.Since(start); rt > _50ms {
		t.Fatalf("w.Write(b) took too long (%v)", rt)
	}

	// Blocking 80-byte write
	w.SetBlocking(true)
	if n, err := w.Write(b[20:]); n != 80 || err != nil {
		t.Fatalf("w.Write(b[20:]) expected 80 (<nil>); got %v (%v)", n, err)
	} else if rt := time.Since(start); rt < _400ms {
		t.Fatalf("w.Write(b[20:]) returned ahead of time (%v)", rt)
	}

	w.SetTransferSize(100)
	status := []Status{w.Status(), nextStatus(w.Monitor)}
	start = status[0].Start

	// Active, Start, Duration, Idle, Bytes, Samples, InstRate, CurRate, AvgRate, PeakRate, BytesRem, TimeRem, Progress
	want := []Status{
		Status{true, start, _400ms, 0, 80, 4, 200, 200, 200, 200, 20, _100ms, 80000},
		Status{true, start, _500ms, _100ms, 100, 5, 200, 200, 200, 200, 0, 0, 100000},
	}
	for i, s := range status {
		if !statusesAreEqual(&s, &want[i]) {
			t.Errorf("w.Status(%v)\nexpected: %v\ngot     : %v\n", i, want[i], s)
		}
	}
	if !bytes.Equal(b, w.Writer.(*bytes.Buffer).Bytes()) {
		t.Errorf("w.Write() input doesn't match output")
	}
}

const maxDeviationForDuration = 50 * time.Millisecond
const maxDeviationForRate int64 = 50

// statusesAreEqual returns true if s1 is equal to s2. Equality here means
// general equality of fields except for the duration and rates, which can
// drift due to unpredictable delays (e.g. thread wakes up 25ms after
// `time.Sleep` has ended).
func statusesAreEqual(s1 *Status, s2 *Status) bool {
	if s1.Active == s2.Active &&
		s1.Start == s2.Start &&
		durationsAreEqual(s1.Duration, s2.Duration, maxDeviationForDuration) &&
		s1.Idle == s2.Idle &&
		s1.Bytes == s2.Bytes &&
		s1.Samples == s2.Samples &&
		ratesAreEqual(s1.InstRate, s2.InstRate, maxDeviationForRate) &&
		ratesAreEqual(s1.CurRate, s2.CurRate, maxDeviationForRate) &&
		ratesAreEqual(s1.AvgRate, s2.AvgRate, maxDeviationForRate) &&
		ratesAreEqual(s1.PeakRate, s2.PeakRate, maxDeviationForRate) &&
		s1.BytesRem == s2.BytesRem &&
		durationsAreEqual(s1.TimeRem, s2.TimeRem, maxDeviationForDuration) &&
		s1.Progress == s2.Progress {
		return true
	}
	return false
}

func durationsAreEqual(d1 time.Duration, d2 time.Duration, maxDeviation time.Duration) bool {
	if d2-d1 <= maxDeviation {
		return true
	}
	return false
}

func ratesAreEqual(r1 int64, r2 int64, maxDeviation int64) bool {
	sub := r1 - r2
	if sub < 0 {
		sub = -sub
	}
	if sub <= maxDeviation {
		return true
	}
	return false
}
<|endoftext|>


<commit_before>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package tls

import (
	"crypto/x509"
	"reflect"
	"syscall"
	"unsafe"
)

func loadStore(roots *x509.CertPool, name string) {
	store, errno := syscall.CertOpenSystemStore(syscall.InvalidHandle, syscall.StringToUTF16Ptr(name))
	if errno != 0 {
		return
	}

	var prev *syscall.CertContext
	for {
		cur := syscall.CertEnumCertificatesInStore(store, prev)
		if cur == nil {
			break
		}

		var buf []byte
		hdrp := (*reflect.SliceHeader)(unsafe.Pointer(&buf))
		hdrp.Data = cur.EncodedCert
		hdrp.Len = int(cur.Length)
		hdrp.Cap = int(cur.Length)

		cert, err := x509.ParseCertificate(buf)
		if err != nil {
			continue
		}

		roots.AddCert(cert)
		prev = cur
	}

	syscall.CertCloseStore(store, 0)
}

func initDefaultRoots() {
	// TODO(brainman): To be fixed
	return

	roots := x509.NewCertPool()

	// Roots
	loadStore(roots, "ROOT")

	// Intermediates
	loadStore(roots, "CA")

	varDefaultRoots = roots
}
<commit_msg>crypto/tls: fix broken looping code in windows root CA fetcher<commit_after>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package tls

import (
	"crypto/x509"
	"reflect"
	"syscall"
	"unsafe"
)

func loadStore(roots *x509.CertPool, name string) {
	store, errno := syscall.CertOpenSystemStore(syscall.InvalidHandle, syscall.StringToUTF16Ptr(name))
	if errno != 0 {
		return
	}

	var cert *syscall.CertContext
	for {
		cert = syscall.CertEnumCertificatesInStore(store, cert)
		if cert == nil {
			break
		}

		var asn1Slice []byte
		hdrp := (*reflect.SliceHeader)(unsafe.Pointer(&asn1Slice))
		hdrp.Data = cert.EncodedCert
		hdrp.Len = int(cert.Length)
		hdrp.Cap = int(cert.Length)

		buf := make([]byte, len(asn1Slice))
		copy(buf, asn1Slice)

		if cert, err := x509.ParseCertificate(buf); err == nil {
			roots.AddCert(cert)
		}
	}

	syscall.CertCloseStore(store, 0)
}

func initDefaultRoots() {
	roots := x509.NewCertPool()

	// Roots
	loadStore(roots, "ROOT")

	// Intermediates
	loadStore(roots, "CA")

	varDefaultRoots = roots
}
<|endoftext|>


<commit_before>package aws

import (
	"encoding/json"
	"fmt"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/sns"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/mock"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
	"time"
)

func TestSNSReadyAndPublishSuccess(t *testing.T) {

	ss, m, mv, _ := SetUpTestSNSServer(t)

	testSubscribe(t, m, ss)
	testSubConf(t, m, mv, ss)
	testPublish(t, m, ss)

	m.AssertExpectations(t)
}

func TestSNSReadyToNotReadySwitchAndBack(t *testing.T) {
	expectedSubArn := "pending confirmation"

	ss, m, mv, _ := SetUpTestSNSServer(t)

	testSubscribe(t, m, ss)
	testSubConf(t, m, mv, ss)
	testPublish(t, m, ss)

	// mocking SNS subscribe response
	m.On("Subscribe", mock.AnythingOfType("*sns.SubscribeInput")).Return(&sns.SubscribeOutput{
		SubscriptionArn: &expectedSubArn}, nil)
	// Subscribe again will not change the ready state
	// as the subArn value stored locally and in AWS are still the same
	ss.Subscribe()

	assert.Equal(t, ss.subscriptionArn.Load().(string), "testSubscriptionArn")

	// Test Publish
	testPublish(t, m, ss)

	// SNS Ready and Publish again
	testSubConf(t, m, mv, ss)
	testPublish(t, m, ss)

	m.AssertExpectations(t)
}

func testSubscribe(t *testing.T, m *MockSVC, ss *SNSServer) {
	expectedSubArn := "pending confirmation"

	// mocking SNS subscribe response
	m.On("Subscribe", mock.AnythingOfType("*sns.SubscribeInput")).Return(&sns.SubscribeOutput{
		SubscriptionArn: &expectedSubArn}, nil)
	ss.PrepareAndStart()
	assert.Nil(t, ss.subscriptionArn.Load())
}

func testSubConf(t *testing.T, m *MockSVC, mv *MockValidator, ss *SNSServer) {
	assert := assert.New(t)

	confSubArn := "testSubscriptionArn"

	// mocking SNS ConfirmSubscription response
	m.On("ConfirmSubscription", mock.AnythingOfType("*sns.ConfirmSubscriptionInput")).Return(&sns.ConfirmSubscriptionOutput{
		SubscriptionArn: &confSubArn}, nil)
	mv.On("Validate", mock.AnythingOfType("*aws.SNSMessage")).Return(true, nil).Once()

	// mocking SNS ListSubscriptionsByTopic response to empty list
	m.On("ListSubscriptionsByTopic", mock.AnythingOfType("*sns.ListSubscriptionsByTopicInput")).Return(
		&sns.ListSubscriptionsByTopicOutput{Subscriptions: []*sns.Subscription{}}, nil)

	// Mocking AWS SubscriptionConfirmation POST call using http client
	req := httptest.NewRequest("POST", ss.SelfUrl.String()+ss.Config.Sns.UrlPath, strings.NewReader(TEST_SUB_MSG))
	req.Header.Add("x-amz-sns-message-type", "SubscriptionConfirmation")

	w := httptest.NewRecorder()
	ss.SubscribeConfirmHandle(w, req)
	resp := w.Result()

	assert.Equal(http.StatusOK, resp.StatusCode)
	time.Sleep(1 * time.Second)
	assert.Equal(ss.subscriptionArn.Load().(string), confSubArn)

}

func testPublish(t *testing.T, m *MockSVC, ss *SNSServer) {
	// mocking SNS Publish response
	m.On("Publish", mock.AnythingOfType("*sns.PublishInput")).Return(&sns.PublishOutput{}, nil)

	ss.PublishMessage(TEST_HOOK)

	// wait such that listenAndPublishMessage go routine will publish message
	time.Sleep(1 * time.Second)

}

func TestSNSSubConfValidateErr(t *testing.T) {
	assert := assert.New(t)

	ss, m, mv, _ := SetUpTestSNSServer(t)

	testSubscribe(t, m, ss)

	mv.On("Validate", mock.AnythingOfType("*aws.SNSMessage")).Return(false,
		fmt.Errorf("%s", SNS_VALIDATION_ERR))

	// Mocking AWS SubscriptionConfirmation POST call using http client
	req := httptest.NewRequest("POST", ss.SelfUrl.String()+ss.Config.Sns.UrlPath, strings.NewReader(TEST_SUB_MSG))
	req.Header.Add("x-amz-sns-message-type", "SubscriptionConfirmation")

	w := httptest.NewRecorder()
	ss.SubscribeConfirmHandle(w, req)
	resp := w.Result()

	assert.Equal(http.StatusBadRequest, resp.StatusCode)
	errMsg := new(ErrResp)
	errResp, _ := ioutil.ReadAll(resp.Body)
	json.Unmarshal([]byte(errResp), errMsg)

	assert.Equal(errMsg.Code, http.StatusBadRequest)
	assert.Equal(errMsg.Message, SNS_VALIDATION_ERR)

	m.AssertExpectations(t)
	mv.AssertExpectations(t)
	assert.Nil(t, ss.subscriptionArn.Load())
}

func TestSNSReadyUnsubscribeOldSubscriptions(t *testing.T) {
	assert := assert.New(t)
	ss, m, mv, _ := SetUpTestSNSServer(t)

	testSubscribe(t, m, ss)

	confSubArn := "testSubscriptionArn"

	// mocking SNS ConfirmSubscription response
	m.On("ConfirmSubscription", mock.AnythingOfType("*sns.ConfirmSubscriptionInput")).Return(&sns.ConfirmSubscriptionOutput{
		SubscriptionArn: &confSubArn}, nil)
	mv.On("Validate", mock.AnythingOfType("*aws.SNSMessage")).Return(true, nil).Once()

	// mocking SNS ListSubscriptionsByTopic response to list
	sub1 := &sns.Subscription{
		Endpoint:        aws.String("http://host:port/api/v2/aws/sns/1503357402"),
		TopicArn:        aws.String("arn:aws:sns:us-east-1:1234:test-topic"),
		SubscriptionArn: aws.String("test1"),
	}
	sub2 := &sns.Subscription{
		Endpoint:        aws.String("http://host:port/api/v2/aws/sns"),
		TopicArn:        aws.String("arn:aws:sns:us-east-1:1234:test-topic"),
		SubscriptionArn: aws.String("test2"),
	}
	m.On("ListSubscriptionsByTopic", mock.AnythingOfType("*sns.ListSubscriptionsByTopicInput")).Return(
		&sns.ListSubscriptionsByTopicOutput{Subscriptions: []*sns.Subscription{sub1, sub2}}, nil)

	// mocking Unsubscribe call
	m.On("Unsubscribe", &sns.UnsubscribeInput{SubscriptionArn: aws.String("test2")}).Return(&sns.UnsubscribeOutput{}, nil)

	// Mocking AWS SubscriptionConfirmation POST call using http client
	req := httptest.NewRequest("POST", ss.SelfUrl.String()+ss.Config.Sns.UrlPath, strings.NewReader(TEST_SUB_MSG))
	req.Header.Add("x-amz-sns-message-type", "SubscriptionConfirmation")

	w := httptest.NewRecorder()
	ss.SubscribeConfirmHandle(w, req)
	resp := w.Result()

	assert.Equal(http.StatusOK, resp.StatusCode)

	// wait such that listenSubscriptionData go routine will update the SubscriptionArn value
	time.Sleep(1 * time.Second)

	assert.Equal(ss.subscriptionArn.Load().(string), confSubArn)

	m.AssertExpectations(t)
	mv.AssertExpectations(t)
}
<commit_msg>Fix test<commit_after>package aws

import (
	"encoding/json"
	"fmt"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/sns"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/mock"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
	"time"
)

func TestSNSReadyAndPublishSuccess(t *testing.T) {

	ss, m, mv, _ := SetUpTestSNSServer(t)

	testSubscribe(t, m, ss)
	testSubConf(t, m, mv, ss)
	testPublish(t, m, ss)

	m.AssertExpectations(t)
}

func TestSNSReadyToNotReadySwitchAndBack(t *testing.T) {
	expectedSubArn := "pending confirmation"

	ss, m, mv, _ := SetUpTestSNSServer(t)

	testSubscribe(t, m, ss)
	testSubConf(t, m, mv, ss)
	testPublish(t, m, ss)

	// mocking SNS subscribe response
	m.On("Subscribe", mock.AnythingOfType("*sns.SubscribeInput")).Return(&sns.SubscribeOutput{
		SubscriptionArn: &expectedSubArn}, nil)
	// Subscribe again will not change the ready state
	// as the subArn value stored locally and in AWS are still the same
	ss.Subscribe()

	assert.Equal(t, ss.subscriptionArn.Load().(string), "testSubscriptionArn")

	// Test Publish
	testPublish(t, m, ss)

	// SNS Ready and Publish again
	testSubConf(t, m, mv, ss)
	testPublish(t, m, ss)

	m.AssertExpectations(t)
}

func testSubscribe(t *testing.T, m *MockSVC, ss *SNSServer) {
	expectedSubArn := "pending confirmation"

	// mocking SNS subscribe response
	m.On("Subscribe", mock.AnythingOfType("*sns.SubscribeInput")).Return(&sns.SubscribeOutput{
		SubscriptionArn: &expectedSubArn}, nil)
	ss.PrepareAndStart()
	assert.Nil(t, ss.subscriptionArn.Load())
}

func testSubConf(t *testing.T, m *MockSVC, mv *MockValidator, ss *SNSServer) {
	assert := assert.New(t)

	confSubArn := "testSubscriptionArn"

	// mocking SNS ConfirmSubscription response
	m.On("ConfirmSubscription", mock.AnythingOfType("*sns.ConfirmSubscriptionInput")).Return(&sns.ConfirmSubscriptionOutput{
		SubscriptionArn: &confSubArn}, nil)
	mv.On("Validate", mock.AnythingOfType("*aws.SNSMessage")).Return(true, nil).Once()

	// mocking SNS ListSubscriptionsByTopic response to empty list
	m.On("ListSubscriptionsByTopic", mock.AnythingOfType("*sns.ListSubscriptionsByTopicInput")).Return(
		&sns.ListSubscriptionsByTopicOutput{Subscriptions: []*sns.Subscription{}}, nil)

	// Mocking AWS SubscriptionConfirmation POST call using http client
	req := httptest.NewRequest("POST", ss.SelfUrl.String()+ss.Config.Sns.UrlPath, strings.NewReader(TEST_SUB_MSG))
	req.Header.Add("x-amz-sns-message-type", "SubscriptionConfirmation")

	w := httptest.NewRecorder()
	ss.SubscribeConfirmHandle(w, req)
	resp := w.Result()

	assert.Equal(http.StatusOK, resp.StatusCode)
	time.Sleep(1 * time.Second)
	assert.Equal(ss.subscriptionArn.Load().(string), confSubArn)

}

func testPublish(t *testing.T, m *MockSVC, ss *SNSServer) {
	// mocking SNS Publish response
	m.On("Publish", mock.AnythingOfType("*sns.PublishInput")).Return(&sns.PublishOutput{}, nil)

	ss.PublishMessage(TEST_HOOK)

	// wait such that listenAndPublishMessage go routine will publish message
	time.Sleep(1 * time.Second)

}

func TestSNSSubConfValidateErr(t *testing.T) {
	assert := assert.New(t)

	ss, m, mv, _ := SetUpTestSNSServer(t)

	testSubscribe(t, m, ss)

	mv.On("Validate", mock.AnythingOfType("*aws.SNSMessage")).Return(false,
		fmt.Errorf("%s", SNS_VALIDATION_ERR))

	// Mocking AWS SubscriptionConfirmation POST call using http client
	req := httptest.NewRequest("POST", ss.SelfUrl.String()+ss.Config.Sns.UrlPath, strings.NewReader(TEST_SUB_MSG))
	req.Header.Add("x-amz-sns-message-type", "SubscriptionConfirmation")

	w := httptest.NewRecorder()
	ss.SubscribeConfirmHandle(w, req)
	resp := w.Result()

	assert.Equal(http.StatusBadRequest, resp.StatusCode)
	errMsg := new(ErrResp)
	errResp, _ := ioutil.ReadAll(resp.Body)
	json.Unmarshal([]byte(errResp), errMsg)

	assert.Equal(errMsg.Code, http.StatusBadRequest)
	assert.Equal(errMsg.Message, SNS_VALIDATION_ERR)

	m.AssertExpectations(t)
	mv.AssertExpectations(t)
}

func TestSNSReadyUnsubscribeOldSubscriptions(t *testing.T) {
	assert := assert.New(t)
	ss, m, mv, _ := SetUpTestSNSServer(t)

	testSubscribe(t, m, ss)

	confSubArn := "testSubscriptionArn"

	// mocking SNS ConfirmSubscription response
	m.On("ConfirmSubscription", mock.AnythingOfType("*sns.ConfirmSubscriptionInput")).Return(&sns.ConfirmSubscriptionOutput{
		SubscriptionArn: &confSubArn}, nil)
	mv.On("Validate", mock.AnythingOfType("*aws.SNSMessage")).Return(true, nil).Once()

	// mocking SNS ListSubscriptionsByTopic response to list
	sub1 := &sns.Subscription{
		Endpoint:        aws.String("http://host:port/api/v2/aws/sns/1503357402"),
		TopicArn:        aws.String("arn:aws:sns:us-east-1:1234:test-topic"),
		SubscriptionArn: aws.String("test1"),
	}
	sub2 := &sns.Subscription{
		Endpoint:        aws.String("http://host:port/api/v2/aws/sns"),
		TopicArn:        aws.String("arn:aws:sns:us-east-1:1234:test-topic"),
		SubscriptionArn: aws.String("test2"),
	}
	m.On("ListSubscriptionsByTopic", mock.AnythingOfType("*sns.ListSubscriptionsByTopicInput")).Return(
		&sns.ListSubscriptionsByTopicOutput{Subscriptions: []*sns.Subscription{sub1, sub2}}, nil)

	// mocking Unsubscribe call
	m.On("Unsubscribe", &sns.UnsubscribeInput{SubscriptionArn: aws.String("test2")}).Return(&sns.UnsubscribeOutput{}, nil)

	// Mocking AWS SubscriptionConfirmation POST call using http client
	req := httptest.NewRequest("POST", ss.SelfUrl.String()+ss.Config.Sns.UrlPath, strings.NewReader(TEST_SUB_MSG))
	req.Header.Add("x-amz-sns-message-type", "SubscriptionConfirmation")

	w := httptest.NewRecorder()
	ss.SubscribeConfirmHandle(w, req)
	resp := w.Result()

	assert.Equal(http.StatusOK, resp.StatusCode)

	// wait such that listenSubscriptionData go routine will update the SubscriptionArn value
	time.Sleep(1 * time.Second)

	assert.Equal(ss.subscriptionArn.Load().(string), confSubArn)

	m.AssertExpectations(t)
	mv.AssertExpectations(t)
}
<|endoftext|>


<commit_before>package mocks

import (
	"sync"
	"sync/atomic"

	"github.com/Shopify/sarama"
)

// Consumer implements sarama's Consumer interface for testing purposes.
// Before you can start consuming from this consumer, you have to register
// topic/partitions using ExpectConsumePartition, and set expectations on them.
type Consumer struct {
	l                  sync.Mutex
	t                  ErrorReporter
	config             *sarama.Config
	partitionConsumers map[string]map[int32]*PartitionConsumer
	metadata           map[string][]int32
}

// NewConsumer returns a new mock Consumer instance. The t argument should
// be the *testing.T instance of your test method. An error will be written to it if
// an expectation is violated. The config argument is currently unused and can be set to nil.
func NewConsumer(t ErrorReporter, config *sarama.Config) *Consumer {
	if config == nil {
		config = sarama.NewConfig()
	}

	c := &Consumer{
		t:                  t,
		config:             config,
		partitionConsumers: make(map[string]map[int32]*PartitionConsumer),
	}
	return c
}

///////////////////////////////////////////////////
// Consumer interface implementation
///////////////////////////////////////////////////

// ConsumePartition implements the ConsumePartition method from the sarama.Consumer interface.
// Before you can start consuming a partition, you have to set expectations on it using
// ExpectConsumePartition. You can only consume a partition once per consumer.
func (c *Consumer) ConsumePartition(topic string, partition int32, offset int64) (sarama.PartitionConsumer, error) {
	c.l.Lock()
	defer c.l.Unlock()

	if c.partitionConsumers[topic] == nil || c.partitionConsumers[topic][partition] == nil {
		c.t.Errorf("No expectations set for %s/%d", topic, partition)
		return nil, errOutOfExpectations
	}

	pc := c.partitionConsumers[topic][partition]
	if pc.consumed {
		return nil, sarama.ConfigurationError("The topic/partition is already being consumed")
	}

	if pc.offset != AnyOffset && pc.offset != offset {
		c.t.Errorf("Unexpected offset when calling ConsumePartition for %s/%d. Expected %d, got %d.", topic, partition, pc.offset, offset)
	}

	pc.consumed = true
	return pc, nil
}

// Topics returns a list of topics, as registered with SetMetadata
func (c *Consumer) Topics() ([]string, error) {
	c.l.Lock()
	defer c.l.Unlock()

	if c.metadata == nil {
		c.t.Errorf("Unexpected call to Topics. Initialize the mock's topic metadata with SetMetadata.")
		return nil, sarama.ErrOutOfBrokers
	}

	var result []string
	for topic := range c.metadata {
		result = append(result, topic)
	}
	return result, nil
}

// Partitions returns the list of parititons for the given topic, as registered with SetMetadata
func (c *Consumer) Partitions(topic string) ([]int32, error) {
	c.l.Lock()
	defer c.l.Unlock()

	if c.metadata == nil {
		c.t.Errorf("Unexpected call to Partitions. Initialize the mock's topic metadata with SetMetadata.")
		return nil, sarama.ErrOutOfBrokers
	}
	if c.metadata[topic] == nil {
		return nil, sarama.ErrUnknownTopicOrPartition
	}

	return c.metadata[topic], nil
}

func (c *Consumer) HighWaterMarks() map[string]map[int32]int64 {
	c.l.Lock()
	defer c.l.Unlock()

	hwms := make(map[string]map[int32]int64, len(c.partitionConsumers))
	for topic, partitionConsumers := range c.partitionConsumers {
		hwm := make(map[int32]int64, len(partitionConsumers))
		for partition, pc := range partitionConsumers {
			hwm[partition] = pc.HighWaterMarkOffset()
		}
		hwms[topic] = hwm
	}

	return hwms
}

// Close implements the Close method from the sarama.Consumer interface. It will close
// all registered PartitionConsumer instances.
func (c *Consumer) Close() error {
	c.l.Lock()
	defer c.l.Unlock()

	for _, partitions := range c.partitionConsumers {
		for _, partitionConsumer := range partitions {
			_ = partitionConsumer.Close()
		}
	}

	return nil
}

///////////////////////////////////////////////////
// Expectation API
///////////////////////////////////////////////////

// SetTopicMetadata sets the clusters topic/partition metadata,
// which will be returned by Topics() and Partitions().
func (c *Consumer) SetTopicMetadata(metadata map[string][]int32) {
	c.l.Lock()
	defer c.l.Unlock()

	c.metadata = metadata
}

// ExpectConsumePartition will register a topic/partition, so you can set expectations on it.
// The registered PartitionConsumer will be returned, so you can set expectations
// on it using method chaining. Once a topic/partition is registered, you are
// expected to start consuming it using ConsumePartition. If that doesn't happen,
// an error will be written to the error reporter once the mock consumer is closed. It will
// also expect that the
func (c *Consumer) ExpectConsumePartition(topic string, partition int32, offset int64) *PartitionConsumer {
	c.l.Lock()
	defer c.l.Unlock()

	if c.partitionConsumers[topic] == nil {
		c.partitionConsumers[topic] = make(map[int32]*PartitionConsumer)
	}

	if c.partitionConsumers[topic][partition] == nil {
		c.partitionConsumers[topic][partition] = &PartitionConsumer{
			t:         c.t,
			topic:     topic,
			partition: partition,
			offset:    offset,
			messages:  make(chan *sarama.ConsumerMessage, c.config.ChannelBufferSize),
			errors:    make(chan *sarama.ConsumerError, c.config.ChannelBufferSize),
		}
	}

	return c.partitionConsumers[topic][partition]
}

///////////////////////////////////////////////////
// PartitionConsumer mock type
///////////////////////////////////////////////////

// PartitionConsumer implements sarama's PartitionConsumer interface for testing purposes.
// It is returned by the mock Consumers ConsumePartitionMethod, but only if it is
// registered first using the Consumer's ExpectConsumePartition method. Before consuming the
// Errors and Messages channel, you should specify what values will be provided on these
// channels using YieldMessage and YieldError.
type PartitionConsumer struct {
	l                       sync.Mutex
	t                       ErrorReporter
	topic                   string
	partition               int32
	offset                  int64
	messages                chan *sarama.ConsumerMessage
	errors                  chan *sarama.ConsumerError
	singleClose             sync.Once
	consumed                bool
	errorsShouldBeDrained   bool
	messagesShouldBeDrained bool
	highWaterMarkOffset     int64
}

///////////////////////////////////////////////////
// PartitionConsumer interface implementation
///////////////////////////////////////////////////

// AsyncClose implements the AsyncClose method from the sarama.PartitionConsumer interface.
func (pc *PartitionConsumer) AsyncClose() {
	pc.singleClose.Do(func() {
		close(pc.messages)
		close(pc.errors)
	})
}

// Close implements the Close method from the sarama.PartitionConsumer interface. It will
// verify whether the partition consumer was actually started.
func (pc *PartitionConsumer) Close() error {
	if !pc.consumed {
		pc.t.Errorf("Expectations set on %s/%d, but no partition consumer was started.", pc.topic, pc.partition)
		return errPartitionConsumerNotStarted
	}

	if pc.errorsShouldBeDrained && len(pc.errors) > 0 {
		pc.t.Errorf("Expected the errors channel for %s/%d to be drained on close, but found %d errors.", pc.topic, pc.partition, len(pc.errors))
	}

	if pc.messagesShouldBeDrained && len(pc.messages) > 0 {
		pc.t.Errorf("Expected the messages channel for %s/%d to be drained on close, but found %d messages.", pc.topic, pc.partition, len(pc.messages))
	}

	pc.AsyncClose()

	var (
		closeErr error
		wg       sync.WaitGroup
	)

	wg.Add(1)
	go func() {
		defer wg.Done()

		var errs = make(sarama.ConsumerErrors, 0)
		for err := range pc.errors {
			errs = append(errs, err)
		}

		if len(errs) > 0 {
			closeErr = errs
		}
	}()

	wg.Add(1)
	go func() {
		defer wg.Done()
		for range pc.messages {
			// drain
		}
	}()

	wg.Wait()
	return closeErr
}

// Errors implements the Errors method from the sarama.PartitionConsumer interface.
func (pc *PartitionConsumer) Errors() <-chan *sarama.ConsumerError {
	return pc.errors
}

// Messages implements the Messages method from the sarama.PartitionConsumer interface.
func (pc *PartitionConsumer) Messages() <-chan *sarama.ConsumerMessage {
	return pc.messages
}

func (pc *PartitionConsumer) HighWaterMarkOffset() int64 {
	return atomic.LoadInt64(&pc.highWaterMarkOffset) + 1
}

///////////////////////////////////////////////////
// Expectation API
///////////////////////////////////////////////////

// YieldMessage will yield a messages Messages channel of this partition consumer
// when it is consumed. By default, the mock consumer will not verify whether this
// message was consumed from the Messages channel, because there are legitimate
// reasons forthis not to happen. ou can call ExpectMessagesDrainedOnClose so it will
// verify that the channel is empty on close.
func (pc *PartitionConsumer) YieldMessage(msg *sarama.ConsumerMessage) {
	pc.l.Lock()
	defer pc.l.Unlock()

	msg.Topic = pc.topic
	msg.Partition = pc.partition
	msg.Offset = atomic.AddInt64(&pc.highWaterMarkOffset, 1)

	pc.messages <- msg
}

// YieldError will yield an error on the Errors channel of this partition consumer
// when it is consumed. By default, the mock consumer will not verify whether this error was
// consumed from the Errors channel, because there are legitimate reasons for this
// not to happen. You can call ExpectErrorsDrainedOnClose so it will verify that
// the channel is empty on close.
func (pc *PartitionConsumer) YieldError(err error) {
	pc.errors <- &sarama.ConsumerError{
		Topic:     pc.topic,
		Partition: pc.partition,
		Err:       err,
	}
}

// ExpectMessagesDrainedOnClose sets an expectation on the partition consumer
// that the messages channel will be fully drained when Close is called. If this
// expectation is not met, an error is reported to the error reporter.
func (pc *PartitionConsumer) ExpectMessagesDrainedOnClose() {
	pc.messagesShouldBeDrained = true
}

// ExpectErrorsDrainedOnClose sets an expectation on the partition consumer
// that the errors channel will be fully drained when Close is called. If this
// expectation is not met, an error is reported to the error reporter.
func (pc *PartitionConsumer) ExpectErrorsDrainedOnClose() {
	pc.errorsShouldBeDrained = true
}
<commit_msg>Fix memory alignment of int64 variable used as atomic.<commit_after>package mocks

import (
	"sync"
	"sync/atomic"

	"github.com/Shopify/sarama"
)

// Consumer implements sarama's Consumer interface for testing purposes.
// Before you can start consuming from this consumer, you have to register
// topic/partitions using ExpectConsumePartition, and set expectations on them.
type Consumer struct {
	l                  sync.Mutex
	t                  ErrorReporter
	config             *sarama.Config
	partitionConsumers map[string]map[int32]*PartitionConsumer
	metadata           map[string][]int32
}

// NewConsumer returns a new mock Consumer instance. The t argument should
// be the *testing.T instance of your test method. An error will be written to it if
// an expectation is violated. The config argument is currently unused and can be set to nil.
func NewConsumer(t ErrorReporter, config *sarama.Config) *Consumer {
	if config == nil {
		config = sarama.NewConfig()
	}

	c := &Consumer{
		t:                  t,
		config:             config,
		partitionConsumers: make(map[string]map[int32]*PartitionConsumer),
	}
	return c
}

///////////////////////////////////////////////////
// Consumer interface implementation
///////////////////////////////////////////////////

// ConsumePartition implements the ConsumePartition method from the sarama.Consumer interface.
// Before you can start consuming a partition, you have to set expectations on it using
// ExpectConsumePartition. You can only consume a partition once per consumer.
func (c *Consumer) ConsumePartition(topic string, partition int32, offset int64) (sarama.PartitionConsumer, error) {
	c.l.Lock()
	defer c.l.Unlock()

	if c.partitionConsumers[topic] == nil || c.partitionConsumers[topic][partition] == nil {
		c.t.Errorf("No expectations set for %s/%d", topic, partition)
		return nil, errOutOfExpectations
	}

	pc := c.partitionConsumers[topic][partition]
	if pc.consumed {
		return nil, sarama.ConfigurationError("The topic/partition is already being consumed")
	}

	if pc.offset != AnyOffset && pc.offset != offset {
		c.t.Errorf("Unexpected offset when calling ConsumePartition for %s/%d. Expected %d, got %d.", topic, partition, pc.offset, offset)
	}

	pc.consumed = true
	return pc, nil
}

// Topics returns a list of topics, as registered with SetMetadata
func (c *Consumer) Topics() ([]string, error) {
	c.l.Lock()
	defer c.l.Unlock()

	if c.metadata == nil {
		c.t.Errorf("Unexpected call to Topics. Initialize the mock's topic metadata with SetMetadata.")
		return nil, sarama.ErrOutOfBrokers
	}

	var result []string
	for topic := range c.metadata {
		result = append(result, topic)
	}
	return result, nil
}

// Partitions returns the list of parititons for the given topic, as registered with SetMetadata
func (c *Consumer) Partitions(topic string) ([]int32, error) {
	c.l.Lock()
	defer c.l.Unlock()

	if c.metadata == nil {
		c.t.Errorf("Unexpected call to Partitions. Initialize the mock's topic metadata with SetMetadata.")
		return nil, sarama.ErrOutOfBrokers
	}
	if c.metadata[topic] == nil {
		return nil, sarama.ErrUnknownTopicOrPartition
	}

	return c.metadata[topic], nil
}

func (c *Consumer) HighWaterMarks() map[string]map[int32]int64 {
	c.l.Lock()
	defer c.l.Unlock()

	hwms := make(map[string]map[int32]int64, len(c.partitionConsumers))
	for topic, partitionConsumers := range c.partitionConsumers {
		hwm := make(map[int32]int64, len(partitionConsumers))
		for partition, pc := range partitionConsumers {
			hwm[partition] = pc.HighWaterMarkOffset()
		}
		hwms[topic] = hwm
	}

	return hwms
}

// Close implements the Close method from the sarama.Consumer interface. It will close
// all registered PartitionConsumer instances.
func (c *Consumer) Close() error {
	c.l.Lock()
	defer c.l.Unlock()

	for _, partitions := range c.partitionConsumers {
		for _, partitionConsumer := range partitions {
			_ = partitionConsumer.Close()
		}
	}

	return nil
}

///////////////////////////////////////////////////
// Expectation API
///////////////////////////////////////////////////

// SetTopicMetadata sets the clusters topic/partition metadata,
// which will be returned by Topics() and Partitions().
func (c *Consumer) SetTopicMetadata(metadata map[string][]int32) {
	c.l.Lock()
	defer c.l.Unlock()

	c.metadata = metadata
}

// ExpectConsumePartition will register a topic/partition, so you can set expectations on it.
// The registered PartitionConsumer will be returned, so you can set expectations
// on it using method chaining. Once a topic/partition is registered, you are
// expected to start consuming it using ConsumePartition. If that doesn't happen,
// an error will be written to the error reporter once the mock consumer is closed. It will
// also expect that the
func (c *Consumer) ExpectConsumePartition(topic string, partition int32, offset int64) *PartitionConsumer {
	c.l.Lock()
	defer c.l.Unlock()

	if c.partitionConsumers[topic] == nil {
		c.partitionConsumers[topic] = make(map[int32]*PartitionConsumer)
	}

	if c.partitionConsumers[topic][partition] == nil {
		c.partitionConsumers[topic][partition] = &PartitionConsumer{
			t:         c.t,
			topic:     topic,
			partition: partition,
			offset:    offset,
			messages:  make(chan *sarama.ConsumerMessage, c.config.ChannelBufferSize),
			errors:    make(chan *sarama.ConsumerError, c.config.ChannelBufferSize),
		}
	}

	return c.partitionConsumers[topic][partition]
}

///////////////////////////////////////////////////
// PartitionConsumer mock type
///////////////////////////////////////////////////

// PartitionConsumer implements sarama's PartitionConsumer interface for testing purposes.
// It is returned by the mock Consumers ConsumePartitionMethod, but only if it is
// registered first using the Consumer's ExpectConsumePartition method. Before consuming the
// Errors and Messages channel, you should specify what values will be provided on these
// channels using YieldMessage and YieldError.
type PartitionConsumer struct {
	highWaterMarkOffset     int64
	l                       sync.Mutex
	t                       ErrorReporter
	topic                   string
	partition               int32
	offset                  int64
	messages                chan *sarama.ConsumerMessage
	errors                  chan *sarama.ConsumerError
	singleClose             sync.Once
	consumed                bool
	errorsShouldBeDrained   bool
	messagesShouldBeDrained bool
}

///////////////////////////////////////////////////
// PartitionConsumer interface implementation
///////////////////////////////////////////////////

// AsyncClose implements the AsyncClose method from the sarama.PartitionConsumer interface.
func (pc *PartitionConsumer) AsyncClose() {
	pc.singleClose.Do(func() {
		close(pc.messages)
		close(pc.errors)
	})
}

// Close implements the Close method from the sarama.PartitionConsumer interface. It will
// verify whether the partition consumer was actually started.
func (pc *PartitionConsumer) Close() error {
	if !pc.consumed {
		pc.t.Errorf("Expectations set on %s/%d, but no partition consumer was started.", pc.topic, pc.partition)
		return errPartitionConsumerNotStarted
	}

	if pc.errorsShouldBeDrained && len(pc.errors) > 0 {
		pc.t.Errorf("Expected the errors channel for %s/%d to be drained on close, but found %d errors.", pc.topic, pc.partition, len(pc.errors))
	}

	if pc.messagesShouldBeDrained && len(pc.messages) > 0 {
		pc.t.Errorf("Expected the messages channel for %s/%d to be drained on close, but found %d messages.", pc.topic, pc.partition, len(pc.messages))
	}

	pc.AsyncClose()

	var (
		closeErr error
		wg       sync.WaitGroup
	)

	wg.Add(1)
	go func() {
		defer wg.Done()

		var errs = make(sarama.ConsumerErrors, 0)
		for err := range pc.errors {
			errs = append(errs, err)
		}

		if len(errs) > 0 {
			closeErr = errs
		}
	}()

	wg.Add(1)
	go func() {
		defer wg.Done()
		for range pc.messages {
			// drain
		}
	}()

	wg.Wait()
	return closeErr
}

// Errors implements the Errors method from the sarama.PartitionConsumer interface.
func (pc *PartitionConsumer) Errors() <-chan *sarama.ConsumerError {
	return pc.errors
}

// Messages implements the Messages method from the sarama.PartitionConsumer interface.
func (pc *PartitionConsumer) Messages() <-chan *sarama.ConsumerMessage {
	return pc.messages
}

func (pc *PartitionConsumer) HighWaterMarkOffset() int64 {
	return atomic.LoadInt64(&pc.highWaterMarkOffset) + 1
}

///////////////////////////////////////////////////
// Expectation API
///////////////////////////////////////////////////

// YieldMessage will yield a messages Messages channel of this partition consumer
// when it is consumed. By default, the mock consumer will not verify whether this
// message was consumed from the Messages channel, because there are legitimate
// reasons forthis not to happen. ou can call ExpectMessagesDrainedOnClose so it will
// verify that the channel is empty on close.
func (pc *PartitionConsumer) YieldMessage(msg *sarama.ConsumerMessage) {
	pc.l.Lock()
	defer pc.l.Unlock()

	msg.Topic = pc.topic
	msg.Partition = pc.partition
	msg.Offset = atomic.AddInt64(&pc.highWaterMarkOffset, 1)

	pc.messages <- msg
}

// YieldError will yield an error on the Errors channel of this partition consumer
// when it is consumed. By default, the mock consumer will not verify whether this error was
// consumed from the Errors channel, because there are legitimate reasons for this
// not to happen. You can call ExpectErrorsDrainedOnClose so it will verify that
// the channel is empty on close.
func (pc *PartitionConsumer) YieldError(err error) {
	pc.errors <- &sarama.ConsumerError{
		Topic:     pc.topic,
		Partition: pc.partition,
		Err:       err,
	}
}

// ExpectMessagesDrainedOnClose sets an expectation on the partition consumer
// that the messages channel will be fully drained when Close is called. If this
// expectation is not met, an error is reported to the error reporter.
func (pc *PartitionConsumer) ExpectMessagesDrainedOnClose() {
	pc.messagesShouldBeDrained = true
}

// ExpectErrorsDrainedOnClose sets an expectation on the partition consumer
// that the errors channel will be fully drained when Close is called. If this
// expectation is not met, an error is reported to the error reporter.
func (pc *PartitionConsumer) ExpectErrorsDrainedOnClose() {
	pc.errorsShouldBeDrained = true
}
<|endoftext|>


<commit_before>package model

import (
	"regexp"
)

type Document struct {
	Title       string
	Description string
	Hash        string

	lastFindIndex int
	rawLines      []string
}

func CreateDocument(repositoryItem *RepositoryItem) *Document {
	doc := Document{
		Hash:          repositoryItem.GetHash(),
		rawLines:      repositoryItem.GetLines(),
		lastFindIndex: 0,
	}

	// parse
	return doc.setTitle().setDescription()
}

func (doc *Document) setTitle() *Document {
	titleRegexp := regexp.MustCompile("\\s*#\\s*(.+)")

	for lineNumber, line := range doc.rawLines[doc.lastFindIndex:] {
		matches := titleRegexp.FindStringSubmatch(line)

		if len(matches) == 2 {
			doc.lastFindIndex = lineNumber
			doc.Title = matches[1]
			return doc
		}
	}

	return doc
}

func (doc *Document) setDescription() *Document {
	descriptionRegexp := regexp.MustCompile("^\\w.+")

	for lineNumber, line := range doc.rawLines[doc.lastFindIndex:] {
		matches := descriptionRegexp.FindStringSubmatch(line)

		if len(matches) == 1 {
			doc.lastFindIndex = lineNumber
			doc.Description = matches[0]
			return doc
		}
	}

	return doc
}
<commit_msg>Trying the make the document parsing more robust and comprehensible with method chaining<commit_after>package model

import (
	"regexp"
)

type Document struct {
	Title       string
	Description string
	Hash        string

	lastFindIndex int
	rawLines      []string
}

func CreateDocument(repositoryItem *RepositoryItem) *Document {
	doc := Document{
		Hash:          repositoryItem.GetHash(),
		rawLines:      repositoryItem.GetLines(),
		lastFindIndex: 0,
	}

	// parse
	return doc.parse()
}

func (doc *Document) parse() *Document {
	return doc.setTitle()
}

func (doc *Document) setTitle() *Document {
	titleRegexp := regexp.MustCompile("\\s*#\\s*(.+)")

	for lineNumber, line := range doc.rawLines[doc.lastFindIndex:] {
		matches := titleRegexp.FindStringSubmatch(line)

		// line must match title pattern
		lineMatchesTitlePattern := len(matches) == 2
		if lineMatchesTitlePattern {

			// is first line or all previous lines are empty
			if lineNumber == 0 || linesMeetCondition(doc.rawLines[0:lineNumber], regexp.MustCompile("^\\s*$")) {

				doc.lastFindIndex = lineNumber
				doc.Title = matches[1]
				return doc.setDescription()

			}
		}
	}

	return doc
}

func (doc *Document) setDescription() *Document {
	descriptionRegexp := regexp.MustCompile("^\\w.+")

	for lineNumber, line := range doc.rawLines[doc.lastFindIndex:] {
		matches := descriptionRegexp.FindStringSubmatch(line)

		// line must match description pattern
		lineMatchesDescriptionPattern := len(matches) == 1
		if lineMatchesDescriptionPattern {
			doc.lastFindIndex = lineNumber
			doc.Description = matches[0]
			return doc
		}
	}

	return doc
}

func linesMeetCondition(lines []string, condition *regexp.Regexp) bool {

	for _, line := range lines {
		if !condition.MatchString(line) {
			return false
		}
	}

	return true
}
<|endoftext|>


<commit_before>package revok

import (
	"context"
	"cred-alert/db"
	"cred-alert/gitclient"
	"cred-alert/kolsch"
	"cred-alert/scanners"
	"cred-alert/scanners/filescanner"
	"cred-alert/sniff"
	"fmt"
	"io"
	"os"
	"time"

	"code.cloudfoundry.org/clock"
	"code.cloudfoundry.org/lager"
	"github.com/tedsuo/ifrit"
)

type headCredentialCounter struct {
	logger               lager.Logger
	repositoryRepository db.RepositoryRepository
	clock                clock.Clock
	interval             time.Duration
	gitClient            gitclient.Client
	sniffer              sniff.Sniffer
}

func NewHeadCredentialCounter(
	logger lager.Logger,
	repositoryRepository db.RepositoryRepository,
	clock clock.Clock,
	interval time.Duration,
	gitClient gitclient.Client,
	sniffer sniff.Sniffer,
) ifrit.Runner {
	return &headCredentialCounter{
		logger:               logger,
		repositoryRepository: repositoryRepository,
		clock:                clock,
		interval:             interval,
		gitClient:            gitClient,
		sniffer:              sniffer,
	}
}

func (c *headCredentialCounter) Run(signals <-chan os.Signal, ready chan<- struct{}) error {
	logger := c.logger.Session("head-credential-counter")
	logger.Info("starting")

	close(ready)

	timer := c.clock.NewTicker(c.interval)

	ctx, cancel := context.WithCancel(context.Background())

	quietLogger := kolsch.NewLogger()

	c.work(logger, quietLogger, cancel, signals)

	for {
		select {
		case <-timer.C():
			c.work(logger, quietLogger, cancel, signals)
		case <-signals:
			cancel()
			return nil
		case <-ctx.Done():
			return nil
		}
	}
}

func (c *headCredentialCounter) work(
	logger lager.Logger,
	quietLogger lager.Logger,
	cancel context.CancelFunc,
	signals <-chan os.Signal,
) {
	repositories, err := c.repositoryRepository.All()
	if err != nil {
		logger.Error("failed-getting-all-repositories", err)
	}

	for i := range repositories {
		select {
		case <-signals:
			cancel()
			return
		default:
			repository := repositories[i]
			repoLogger := logger.WithData(lager.Data{
				"ref":  repository.DefaultBranch,
				"path": repository.Path,
			})

			r, w := io.Pipe()

			errCh := make(chan error)
			go func() {
				errCh <- c.gitClient.AllBlobsForRef(repository.Path, fmt.Sprintf("refs/remotes/origin/%s", repository.DefaultBranch), w)
			}()

			var credCount uint
			_ = c.sniffer.Sniff(
				quietLogger,
				filescanner.New(r, ""), // no filename necessary
				func(lager.Logger, scanners.Violation) error {
					credCount++
					return nil
				})

			if err := <-errCh; err != nil {
				repoLogger.Error("failed-to-get-blobs", err)
				continue
			}

			err := c.repositoryRepository.UpdateCredentialCount(&repository, credCount)
			if err != nil {
				repoLogger.Error("failed-to-update-credential-count", err)
			}
		}
	}
}
<commit_msg>Add success logging to head credential counter<commit_after>package revok

import (
	"context"
	"cred-alert/db"
	"cred-alert/gitclient"
	"cred-alert/kolsch"
	"cred-alert/scanners"
	"cred-alert/scanners/filescanner"
	"cred-alert/sniff"
	"fmt"
	"io"
	"os"
	"time"

	"code.cloudfoundry.org/clock"
	"code.cloudfoundry.org/lager"
	"github.com/tedsuo/ifrit"
)

type headCredentialCounter struct {
	logger               lager.Logger
	repositoryRepository db.RepositoryRepository
	clock                clock.Clock
	interval             time.Duration
	gitClient            gitclient.Client
	sniffer              sniff.Sniffer
}

func NewHeadCredentialCounter(
	logger lager.Logger,
	repositoryRepository db.RepositoryRepository,
	clock clock.Clock,
	interval time.Duration,
	gitClient gitclient.Client,
	sniffer sniff.Sniffer,
) ifrit.Runner {
	return &headCredentialCounter{
		logger:               logger,
		repositoryRepository: repositoryRepository,
		clock:                clock,
		interval:             interval,
		gitClient:            gitClient,
		sniffer:              sniffer,
	}
}

func (c *headCredentialCounter) Run(signals <-chan os.Signal, ready chan<- struct{}) error {
	logger := c.logger.Session("head-credential-counter")
	logger.Info("starting")

	close(ready)

	timer := c.clock.NewTicker(c.interval)

	ctx, cancel := context.WithCancel(context.Background())

	quietLogger := kolsch.NewLogger()

	c.work(logger, quietLogger, cancel, signals)

	for {
		select {
		case <-timer.C():
			c.work(logger, quietLogger, cancel, signals)
		case <-signals:
			cancel()
			return nil
		case <-ctx.Done():
			return nil
		}
	}
}

func (c *headCredentialCounter) work(
	logger lager.Logger,
	quietLogger lager.Logger,
	cancel context.CancelFunc,
	signals <-chan os.Signal,
) {
	repositories, err := c.repositoryRepository.All()
	if err != nil {
		logger.Error("failed-getting-all-repositories", err)
	}

	for i := range repositories {
		select {
		case <-signals:
			cancel()
			return
		default:
			repository := repositories[i]
			repoLogger := logger.WithData(lager.Data{
				"ref":  repository.DefaultBranch,
				"path": repository.Path,
			})

			r, w := io.Pipe()

			errCh := make(chan error)
			go func() {
				errCh <- c.gitClient.AllBlobsForRef(repository.Path, fmt.Sprintf("refs/remotes/origin/%s", repository.DefaultBranch), w)
			}()

			var credCount uint
			_ = c.sniffer.Sniff(
				quietLogger,
				filescanner.New(r, ""), // no filename necessary
				func(lager.Logger, scanners.Violation) error {
					credCount++
					return nil
				})

			if err := <-errCh; err != nil {
				repoLogger.Error("failed-to-get-blobs", err)
				continue
			}

			err := c.repositoryRepository.UpdateCredentialCount(&repository, credCount)
			if err != nil {
				repoLogger.Error("failed-to-update-credential-count", err)
				continue
			}

			repoLogger.Info("updated-credential-count", lager.Data{
				"count": credCount,
			})
		}
	}
}
<|endoftext|>


<commit_before>package model

import (
	"github.com/pkg/errors"
	"github.com/viant/toolbox/data"
	"github.com/viant/toolbox/url"
)

//Workflow repesents a workflow
type Workflow struct {
	Source *url.Resource //source definition of the workflow
	Data   data.Map      //workflow data
	*AbstractNode
	*TasksNode //workflow tasks
}

//Validate validates this workflow
func (w *Workflow) Init() error {
	for _, task := range w.Tasks {
		if w.Logging != nil && task.Logging == nil {
			task.Logging = w.Logging
		}
		if err := task.init(); err != nil {
			return err
		}
	}
	return nil
}

//Validate validates this workflow
func (w *Workflow) Validate() error {
	if len(w.Tasks) == 0 {
		return errors.New("tasks were empty")
	}
	if w.DeferredTask != "" {
		if _, err := w.Task(w.DeferredTask); err != nil {
			return err
		}
	}
	if w.OnErrorTask != "" {
		if _, err := w.Task(w.OnErrorTask); err != nil {
			return err
		}
	}

	return nil
}
<commit_msg>corrected typo<commit_after>package model

import (
	"github.com/pkg/errors"
	"github.com/viant/toolbox/data"
	"github.com/viant/toolbox/url"
)

//Workflow represents a workflow
type Workflow struct {
	Source *url.Resource //source definition of the workflow
	Data   data.Map      //workflow data
	*AbstractNode
	*TasksNode //workflow tasks
}

//Validate validates this workflow
func (w *Workflow) Init() error {
	for _, task := range w.Tasks {
		if w.Logging != nil && task.Logging == nil {
			task.Logging = w.Logging
		}
		if err := task.init(); err != nil {
			return err
		}
	}
	return nil
}

//Validate validates this workflow
func (w *Workflow) Validate() error {
	if len(w.Tasks) == 0 {
		return errors.New("tasks were empty")
	}
	if w.DeferredTask != "" {
		if _, err := w.Task(w.DeferredTask); err != nil {
			return err
		}
	}
	if w.OnErrorTask != "" {
		if _, err := w.Task(w.OnErrorTask); err != nil {
			return err
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package gorma

const modelTmpl = `// {{if .Description}}{{.Description}}{{else}}app.{{gotypename . 0}} storage type{{end}}
// Identifier: {{ $typeName :=  gotypename . 0}}{{$typeName := demodel $typeName}}
{{$td := gotypedef . 0 true false}}type {{$typeName}} {{modeldef $td .}}
{{ $belongsto := index .Metadata "github.com/bketelsen/gorma#belongsto" }}
{{ $m2m := index .Metadata "github.com/bketelsen/gorma#many2many" }}
func {{$typeName}}FromCreatePayload(ctx *app.Create{{demodel $typeName}}Context) {{$typeName}} {
	payload := ctx.Payload
	m := {{$typeName}}{}
	copier.Copy(&m, payload)
	{{ if ne $belongsto "" }} m.{{ $belongsto }}ID=int(ctx.{{ demodel $belongsto }}ID){{end}}
	return m
}

func {{$typeName}}FromUpdatePayload(ctx *app.Update{{demodel $typeName}}Context) {{$typeName}} {
	payload := ctx.Payload
	m := {{$typeName}}{}
	copier.Copy(&m, payload)
	return m
}
func (m {{$typeName}}) ToApp() *app.{{demodel $typeName}} {
	target := app.{{demodel $typeName}}{}
	copier.Copy(&target, &m)
	return &target
}
{{ $roler := index .Metadata "github.com/bketelsen/gorma#roler" }}
{{ if ne $roler "" }}
func (m {{$typeName}}) GetRole() string {
	return m.Role
}
{{end}}

type {{$typeName}}Storage interface {
	List(ctx *app.List{{demodel $typeName}}Context) []{{$typeName}}
	Get(ctx *app.Show{{demodel $typeName }}Context) ({{$typeName}}, error)
	Add(ctx *app.Create{{demodel $typeName}}Context) ({{$typeName}}, error)
	Update(ctx *app.Update{{demodel $typeName}}Context) (error)
	Delete(ctx *app.Delete{{demodel $typeName}}Context) (error)
	{{ storagedef . }}
}

type {{$typeName}}DB struct {
	DB gorm.DB
}
{{ if ne $belongsto "" }}{{$barray := split $belongsto ","}}{{ range $idx, $bt := $barray}}
// would prefer to just pass a context in here, but they're all different, so can't
func {{$typeName}}Filter(parentid int, originaldb *gorm.DB) func(db *gorm.DB) *gorm.DB {
	if parentid > 0 {
		return func(db *gorm.DB) *gorm.DB {
			return db.Where("{{ snake $bt }}_id = ?", parentid)
		}
	} else {
		return func(db *gorm.DB) *gorm.DB {
			return db
		}
	}
}{{end}}{{end}}
func New{{$typeName}}DB(db gorm.DB) *{{$typeName}}DB {
	return &{{$typeName}}DB{DB: db}
}

func (m *{{$typeName}}DB) List(ctx *app.List{{demodel $typeName}}Context) []{{$typeName}} {

	var objs []{{$typeName}}
    {{ if ne $belongsto "" }}m.DB.Scopes({{$typeName}}Filter(ctx.{{demodel $belongsto}}ID, &m.DB)).Find(&objs){{ else }} m.DB.Find(&objs) {{end}}
	return objs
}

func (m *{{$typeName}}DB) Get(ctx *app.Show{{demodel $typeName}}Context) ({{$typeName}}, error) {

	var obj {{$typeName}}

	err := m.DB.Find(&obj, ctx.{{demodel $typeName}}ID).Error
	if err != nil {
		ctx.Error(err.Error())
	}
	return obj, err
}

func (m *{{$typeName}}DB) Add(ctx *app.Create{{demodel $typeName}}Context) ({{$typeName}}, error) {
	model := {{$typeName}}FromCreatePayload(ctx)
	err := m.DB.Create(&model).Error
	return model, err
}
func (m *{{$typeName}}DB) Update(ctx *app.Update{{demodel $typeName}}Context) error {
	getCtx, err := app.NewShow{{demodel $typeName}}Context(ctx.Context)
	if err != nil {
		return  err
	}
	obj, err := m.Get(getCtx)
	if err != nil {
		return  err
	}
	err = m.DB.Model(&obj).Updates({{$typeName}}FromUpdatePayload(ctx)).Error
	if err != nil {
		ctx.Error(err.Error())
	}
	return err
}
func (m *{{$typeName}}DB) Delete(ctx *app.Delete{{demodel $typeName}}Context)  error {
	var obj {{$typeName}}
	err := m.DB.Delete(&obj, ctx.{{demodel $typeName}}ID).Error
	if err != nil {
		ctx.Logger.Error(err.Error())
		return  err
	}
	return  nil
}

{{ if ne $m2m "" }}{{$barray := split $m2m ","}}{{ range $idx, $bt := $barray}}
{{ $pieces := split $bt ":" }} {{ $lowertype := index $pieces 1  }} {{ $lower := lower $lowertype }}  {{ $lowerplural := index $pieces 0  }} {{ $lowerplural := lower $lowerplural}}
func (m *{{$typeName}}DB) Delete{{index $pieces 1}}(ctx *app.Delete{{$lower}}{{$typeName}}Context)  error {
	var obj {{$typeName}}
	err := m.DB.Model(&obj).Association("{{index $pieces 0}}").Delete({{index $pieces 1}}{ID: ctx.{{index $pieces 1}}ID}).Error
	if err != nil {
		ctx.Logger.Error(err.Error())
		return  err
	}
	return  nil
}
func (m *{{$typeName}}DB) Add{{index $pieces 1}}(ctx *app.Add{{$lower}}{{$typeName}}Context) error {
	var obj {{$typeName}}
	err := m.DB.Model(&obj).Association("{{index $pieces 0}}").Append({{index $pieces 1}}{ID: ctx{{index $pieces 1}}ID}).Error
	if err != nil {
		ctx.Logger.Error(err.Error())
		return  err
	}
	return  nil
}
func (m *{{$typeName}}DB) List{{index $pieces 0}}(ctx *app.List{{plural $lowerplural}}{{$typeName}}Context)  {{$typeName}} {
	var obj {{$typeName}}
	err := m.DB.Delete(&obj, ctx.{{demodel $typeName}}ID).Error
	if err != nil {
		ctx.Logger.Error(err.Error())
		return  err
	}
	return  nil
}
{{end}}{{end}}

type Mock{{$typeName}}Storage struct {
	{{$typeName}}List  map[int]{{$typeName}}
	nextID int
	mut sync.Mutex
}
{{if ne $belongsto ""}}{{$barray := split $belongsto ","}}{{ range $idx, $bt := $barray}}
func filter{{$typeName}}By{{$bt}}(parent int, list []{{$typeName}}) []{{$typeName}} {
	filtered := make([]{{$typeName}},0)
	for _,o := range list {
		if o.{{$bt}}ID == int(parent) {
			filtered = append(filtered,o)
		}
	}
	return filtered
}
{{end}}{{end}}

func NewMock{{$typeName}}Storage() *Mock{{$typeName}}Storage {
	ml := make(map[int]{{$typeName}}, 0)
	return &Mock{{$typeName}}Storage{ {{$typeName}}List: ml}
}

func (db *Mock{{$typeName}}Storage) List(ctx *app.List{{demodel $typeName}}Context) []{{$typeName}} {
	var list []{{$typeName}} = make([]{{$typeName}}, 0)
	for _, v := range db.{{$typeName}}List {
		list = append(list, v)
	}
{{if ne $belongsto ""}}
return filter{{$typeName}}By{{$belongsto}}(ctx.{{$belongsto}}ID, list) {{else}}return list{{end}}
}

func (db *Mock{{$typeName}}Storage) Get(ctx *app.Show{{demodel $typeName}}Context) ({{$typeName}}, error) {

	var obj {{$typeName}}

	obj, ok := db.{{$typeName}}List[int(ctx.{{demodel $typeName}}ID)]
	if ok {
		return obj, nil
	} else {
		return obj, errors.New("{{$typeName}} does not exist")
	}
}

func (db *Mock{{$typeName}}Storage) Add(ctx *app.Create{{demodel $typeName}}Context)  ({{$typeName}}, error) {
	u := {{$typeName}}FromCreatePayload(ctx)
	db.mut.Lock()
	db.nextID = db.nextID + 1
	u.ID = db.nextID
	db.mut.Unlock()

	db.{{$typeName}}List[u.ID] = u
	return u, nil
}

func (db *Mock{{$typeName}}Storage) Update(ctx *app.Update{{demodel $typeName}}Context) error {
	id := int(ctx.{{demodel $typeName}}ID)
	_, ok := db.{{$typeName}}List[id]
	if ok {
		db.{{$typeName}}List[id] = {{$typeName}}FromUpdatePayload(ctx)
		return  nil
	} else {
		return errors.New("{{$typeName}} does not exist")
	}
}

func (db *Mock{{$typeName}}Storage) Delete(ctx *app.Delete{{demodel $typeName}}Context)  error {
	_, ok := db.{{$typeName}}List[int(ctx.{{demodel $typeName}}ID)]
	if ok {
		delete(db.{{$typeName}}List, int(ctx.{{demodel $typeName}}ID))
		return  nil
	} else {
		return  errors.New("Could not delete this user")
	}
}
`
<commit_msg>work on delete association<commit_after>package gorma

const modelTmpl = `// {{if .Description}}{{.Description}}{{else}}app.{{gotypename . 0}} storage type{{end}}
// Identifier: {{ $typeName :=  gotypename . 0}}{{$typeName := demodel $typeName}}
{{$td := gotypedef . 0 true false}}type {{$typeName}} {{modeldef $td .}}
{{ $belongsto := index .Metadata "github.com/bketelsen/gorma#belongsto" }}
{{ $m2m := index .Metadata "github.com/bketelsen/gorma#many2many" }}
func {{$typeName}}FromCreatePayload(ctx *app.Create{{demodel $typeName}}Context) {{$typeName}} {
	payload := ctx.Payload
	m := {{$typeName}}{}
	copier.Copy(&m, payload)
	{{ if ne $belongsto "" }} m.{{ $belongsto }}ID=int(ctx.{{ demodel $belongsto }}ID){{end}}
	return m
}

func {{$typeName}}FromUpdatePayload(ctx *app.Update{{demodel $typeName}}Context) {{$typeName}} {
	payload := ctx.Payload
	m := {{$typeName}}{}
	copier.Copy(&m, payload)
	return m
}
func (m {{$typeName}}) ToApp() *app.{{demodel $typeName}} {
	target := app.{{demodel $typeName}}{}
	copier.Copy(&target, &m)
	return &target
}
{{ $roler := index .Metadata "github.com/bketelsen/gorma#roler" }}
{{ if ne $roler "" }}
func (m {{$typeName}}) GetRole() string {
	return m.Role
}
{{end}}

type {{$typeName}}Storage interface {
	List(ctx *app.List{{demodel $typeName}}Context) []{{$typeName}}
	Get(ctx *app.Show{{demodel $typeName }}Context) ({{$typeName}}, error)
	Add(ctx *app.Create{{demodel $typeName}}Context) ({{$typeName}}, error)
	Update(ctx *app.Update{{demodel $typeName}}Context) (error)
	Delete(ctx *app.Delete{{demodel $typeName}}Context) (error)
	{{ storagedef . }}
}

type {{$typeName}}DB struct {
	DB gorm.DB
}
{{ if ne $belongsto "" }}{{$barray := split $belongsto ","}}{{ range $idx, $bt := $barray}}
// would prefer to just pass a context in here, but they're all different, so can't
func {{$typeName}}Filter(parentid int, originaldb *gorm.DB) func(db *gorm.DB) *gorm.DB {
	if parentid > 0 {
		return func(db *gorm.DB) *gorm.DB {
			return db.Where("{{ snake $bt }}_id = ?", parentid)
		}
	} else {
		return func(db *gorm.DB) *gorm.DB {
			return db
		}
	}
}{{end}}{{end}}
func New{{$typeName}}DB(db gorm.DB) *{{$typeName}}DB {
	return &{{$typeName}}DB{DB: db}
}

func (m *{{$typeName}}DB) List(ctx *app.List{{demodel $typeName}}Context) []{{$typeName}} {

	var objs []{{$typeName}}
    {{ if ne $belongsto "" }}m.DB.Scopes({{$typeName}}Filter(ctx.{{demodel $belongsto}}ID, &m.DB)).Find(&objs){{ else }} m.DB.Find(&objs) {{end}}
	return objs
}

func (m *{{$typeName}}DB) Get(ctx *app.Show{{demodel $typeName}}Context) ({{$typeName}}, error) {

	var obj {{$typeName}}

	err := m.DB.Find(&obj, ctx.{{demodel $typeName}}ID).Error
	if err != nil {
		ctx.Error(err.Error())
	}
	return obj, err
}

func (m *{{$typeName}}DB) Add(ctx *app.Create{{demodel $typeName}}Context) ({{$typeName}}, error) {
	model := {{$typeName}}FromCreatePayload(ctx)
	err := m.DB.Create(&model).Error
	return model, err
}
func (m *{{$typeName}}DB) Update(ctx *app.Update{{demodel $typeName}}Context) error {
	getCtx, err := app.NewShow{{demodel $typeName}}Context(ctx.Context)
	if err != nil {
		return  err
	}
	obj, err := m.Get(getCtx)
	if err != nil {
		return  err
	}
	err = m.DB.Model(&obj).Updates({{$typeName}}FromUpdatePayload(ctx)).Error
	if err != nil {
		ctx.Error(err.Error())
	}
	return err
}
func (m *{{$typeName}}DB) Delete(ctx *app.Delete{{demodel $typeName}}Context)  error {
	var obj {{$typeName}}
	err := m.DB.Delete(&obj, ctx.{{demodel $typeName}}ID).Error
	if err != nil {
		ctx.Logger.Error(err.Error())
		return  err
	}
	return  nil
}

{{ if ne $m2m "" }}{{$barray := split $m2m ","}}{{ range $idx, $bt := $barray}}
{{ $pieces := split $bt ":" }} {{ $lowertype := index $pieces 1  }} {{ $lower := lower $lowertype }}  {{ $lowerplural := index $pieces 0  }} {{ $lowerplural := lower $lowerplural}}
func (m *{{$typeName}}DB) Delete{{index $pieces 1}}(ctx *app.Delete{{$lower}}{{$typeName}}Context)  error {
	var obj {{$typeName}}
	err := m.DB.Model(&obj).Association("{{index $pieces 0}}").Delete({{index $pieces 1}}{ID: ctx.{{index $pieces 1}}ID}).Error
	if err != nil {
		ctx.Logger.Error(err.Error())
		return  err
	}
	return  nil
}
func (m *{{$typeName}}DB) Add{{index $pieces 1}}(ctx *app.Add{{$lower}}{{$typeName}}Context) error {
	var obj {{$typeName}}
	err := m.DB.Model(&obj).Association("{{index $pieces 0}}").Append({{index $pieces 1}}{ID: ctx.Add{{$lower}}{{$typeName}}Payload.ID).Error
	if err != nil {
		ctx.Logger.Error(err.Error())
		return  err
	}
	return  nil
}
func (m *{{$typeName}}DB) List{{index $pieces 0}}(ctx *app.List{{plural $lowerplural}}{{$typeName}}Context)  {{$typeName}} {
	var obj {{$typeName}}
	err := m.DB.Delete(&obj, ctx.{{demodel $typeName}}ID).Error
	if err != nil {
		ctx.Logger.Error(err.Error())
		return  err
	}
	return  nil
}
{{end}}{{end}}

type Mock{{$typeName}}Storage struct {
	{{$typeName}}List  map[int]{{$typeName}}
	nextID int
	mut sync.Mutex
}
{{if ne $belongsto ""}}{{$barray := split $belongsto ","}}{{ range $idx, $bt := $barray}}
func filter{{$typeName}}By{{$bt}}(parent int, list []{{$typeName}}) []{{$typeName}} {
	filtered := make([]{{$typeName}},0)
	for _,o := range list {
		if o.{{$bt}}ID == int(parent) {
			filtered = append(filtered,o)
		}
	}
	return filtered
}
{{end}}{{end}}

func NewMock{{$typeName}}Storage() *Mock{{$typeName}}Storage {
	ml := make(map[int]{{$typeName}}, 0)
	return &Mock{{$typeName}}Storage{ {{$typeName}}List: ml}
}

func (db *Mock{{$typeName}}Storage) List(ctx *app.List{{demodel $typeName}}Context) []{{$typeName}} {
	var list []{{$typeName}} = make([]{{$typeName}}, 0)
	for _, v := range db.{{$typeName}}List {
		list = append(list, v)
	}
{{if ne $belongsto ""}}
return filter{{$typeName}}By{{$belongsto}}(ctx.{{$belongsto}}ID, list) {{else}}return list{{end}}
}

func (db *Mock{{$typeName}}Storage) Get(ctx *app.Show{{demodel $typeName}}Context) ({{$typeName}}, error) {

	var obj {{$typeName}}

	obj, ok := db.{{$typeName}}List[int(ctx.{{demodel $typeName}}ID)]
	if ok {
		return obj, nil
	} else {
		return obj, errors.New("{{$typeName}} does not exist")
	}
}

func (db *Mock{{$typeName}}Storage) Add(ctx *app.Create{{demodel $typeName}}Context)  ({{$typeName}}, error) {
	u := {{$typeName}}FromCreatePayload(ctx)
	db.mut.Lock()
	db.nextID = db.nextID + 1
	u.ID = db.nextID
	db.mut.Unlock()

	db.{{$typeName}}List[u.ID] = u
	return u, nil
}

func (db *Mock{{$typeName}}Storage) Update(ctx *app.Update{{demodel $typeName}}Context) error {
	id := int(ctx.{{demodel $typeName}}ID)
	_, ok := db.{{$typeName}}List[id]
	if ok {
		db.{{$typeName}}List[id] = {{$typeName}}FromUpdatePayload(ctx)
		return  nil
	} else {
		return errors.New("{{$typeName}} does not exist")
	}
}

func (db *Mock{{$typeName}}Storage) Delete(ctx *app.Delete{{demodel $typeName}}Context)  error {
	_, ok := db.{{$typeName}}List[int(ctx.{{demodel $typeName}}ID)]
	if ok {
		delete(db.{{$typeName}}List, int(ctx.{{demodel $typeName}}ID))
		return  nil
	} else {
		return  errors.New("Could not delete this user")
	}
}
`
<|endoftext|>