func GenId() gopter.Gen {
	return func(genParams *gopter.GenParameters) *gopter.GenResult {
		id := genId(genParams)
		genResult := gopter.NewGenResult(id, gopter.NoShrinker)
		return genResult
	}
}
Generator for a valid SagaId or TaskId
package saga
import (
	"fmt"
	"github.com/leanovate/gopter"
	"github.com/leanovate/gopter/gen"
	log "github.com/sirupsen/logrus"
	"github.com/twitter/scoot/common/thrifthelpers"
	"github.com/twitter/scoot/scheduler/api/thrift/gen-go/scoot"
	"math"
)
func genId(genParams *gopter.GenParameters) string {
	const chars = "abcdefghijklmnopqrstuvwxyz0123456789"
	length := int(genParams.NextUint64()%20) + 1
	result := make([]byte, length)
	for i := 0; i < length; i++ {
		result[i] = chars[genParams.Rng.Intn(len(chars))]
	}
	return string(result)
}
func genSagaState(genParams *gopter.GenParameters, includeJob bool) *SagaState {
	sagaId := genId(genParams)
	data, _ := gen.SliceOf(gen.UInt8()).Sample()
	if !includeJob {
		data = []uint8{}
	}
	job := data.([]byte)
	state, err := makeSagaState(sagaId, job)
	if err != nil {
		log.Info(err)
	}
	isAborted := genParams.NextBool()
	state.sagaAborted = isAborted
	numTasks := int(genParams.NextUint64() % 100)
	for i := 0; i < numTasks; i++ {
		taskId := genId(genParams)
		flags := TaskStarted
		if genParams.NextBool() {
			flags = flags | TaskCompleted
			err := genTaskCompletedData(state, taskId, genParams)
			if err != nil {
				log.Infof(fmt.Sprintf("Error generating complete task data for %s, %s", taskId, err.Error()))
			}
		}
		if isAborted {
			if genParams.NextBool() {
				flags = flags | CompTaskStarted
				if genParams.NextBool() {
					flags = flags | CompTaskCompleted
				}
			}
		}
		state.taskState[taskId] = flags
	}
	isCompleted := true
	for _, id := range state.GetTaskIds() {
		if state.IsSagaAborted() {
			if !(state.IsTaskStarted(id) && state.IsCompTaskStarted(id) && state.IsCompTaskCompleted(id)) {
				isCompleted = false
				break
			}
		} else {
			if !(state.IsTaskStarted(id) && state.IsTaskCompleted(id)) {
				isCompleted = false
				break
			}
		}
	}
	if isCompleted && genParams.NextBool() {
		state.sagaCompleted = true
	}
	return state
}
func genTaskCompletedData(state *SagaState, taskID string, genParams *gopter.GenParameters) error {
	runStatus := scoot.RunStatus{}
	runStatus.RunId = taskID
	var statusVal scoot.RunStatusState
	statusVal = scoot.RunStatusState(int64(math.Trunc(math.Abs(float64(genParams.NextInt64()%5)))) + 3)
	if &statusVal == nil {
		statusVal = scoot.RunStatusState_COMPLETE
	} else {
		statusVal = scoot.RunStatusState(statusVal)
	}
	runStatus.Status = statusVal
	exitCodeStatus := int32(genParams.NextInt64() % 4)
	var ok int32 = 0
	var notOk int32 = -1
	if exitCodeStatus == 0 {
		runStatus.ExitCode = &ok
	} else {
		runStatus.ExitCode = &notOk
	}
	t1 := fmt.Sprintf("error %d", genParams.NextInt64())
	runStatus.Error = &t1
	t2 := fmt.Sprintf("error URI %d", genParams.NextInt64())
	runStatus.ErrUri = &t2
	t3 := fmt.Sprintf("out URI %d", genParams.NextInt64())
	runStatus.OutUri = &t3
	messageAsBytes, err := thrifthelpers.JsonSerialize(&runStatus)
	if err != nil {
		return err
	}
	state.addTaskData(taskID, EndTask, messageAsBytes)
	return nil
}