godog/internal/storage/storage.go
Softonik 345831de9a Генерённые функции выдаются в порядке следования в сценарии (часть 2)
Поправлена ошибка нарушения последовательности шагов тестов, сохранённых
в базе  hashicorp/go-memdb@v1.3.2
Проявлялась в Go 1.19.
В Go 1.17, которая в требованиях - проблемы не было.
2022-11-26 01:19:35 +03:00

351 строка
9,8 КиБ
Go

package storage
import (
"fmt"
"sort"
"strconv"
"sync"
"github.com/cucumber/messages-go/v16"
"github.com/hashicorp/go-memdb"
"github.com/cucumber/godog/internal/models"
)
const (
writeMode bool = true
readMode bool = false
tableFeature string = "feature"
tableFeatureIndexURI string = "id"
tablePickle string = "pickle"
tablePickleIndexID string = "id"
tablePickleIndexURI string = "uri"
tablePickleStep string = "pickle_step"
tablePickleStepIndexID string = "id"
tablePickleResult string = "pickle_result"
tablePickleResultIndexPickleID string = "id"
tablePickleStepResult string = "pickle_step_result"
tablePickleStepResultIndexPickleStepID string = "id"
tablePickleStepResultIndexPickleID string = "pickle_id"
tablePickleStepResultIndexStatus string = "status"
tableStepDefintionMatch string = "step_defintion_match"
tableStepDefintionMatchIndexStepID string = "id"
)
// Storage is a thread safe in-mem storage
type Storage struct {
db *memdb.MemDB
testRunStarted models.TestRunStarted
testRunStartedLock *sync.Mutex
}
// NewStorage will create an in-mem storage that
// is used across concurrent runners and formatters
func NewStorage() *Storage {
schema := memdb.DBSchema{
Tables: map[string]*memdb.TableSchema{
tableFeature: {
Name: tableFeature,
Indexes: map[string]*memdb.IndexSchema{
tableFeatureIndexURI: {
Name: tableFeatureIndexURI,
Unique: true,
Indexer: &memdb.StringFieldIndex{Field: "Uri"},
},
},
},
tablePickle: {
Name: tablePickle,
Indexes: map[string]*memdb.IndexSchema{
tablePickleIndexID: {
Name: tablePickleIndexID,
Unique: true,
Indexer: &memdb.StringFieldIndex{Field: "Id"},
},
tablePickleIndexURI: {
Name: tablePickleIndexURI,
Unique: false,
Indexer: &memdb.StringFieldIndex{Field: "Uri"},
},
},
},
tablePickleStep: {
Name: tablePickleStep,
Indexes: map[string]*memdb.IndexSchema{
tablePickleStepIndexID: {
Name: tablePickleStepIndexID,
Unique: true,
Indexer: &memdb.StringFieldIndex{Field: "Id"},
},
},
},
tablePickleResult: {
Name: tablePickleResult,
Indexes: map[string]*memdb.IndexSchema{
tablePickleResultIndexPickleID: {
Name: tablePickleResultIndexPickleID,
Unique: true,
Indexer: &memdb.StringFieldIndex{Field: "PickleID"},
},
},
},
tablePickleStepResult: {
Name: tablePickleStepResult,
Indexes: map[string]*memdb.IndexSchema{
tablePickleStepResultIndexPickleStepID: {
Name: tablePickleStepResultIndexPickleStepID,
Unique: true,
Indexer: &memdb.StringFieldIndex{Field: "PickleStepID"},
},
tablePickleStepResultIndexPickleID: {
Name: tablePickleStepResultIndexPickleID,
Unique: false,
Indexer: &memdb.StringFieldIndex{Field: "PickleID"},
},
tablePickleStepResultIndexStatus: {
Name: tablePickleStepResultIndexStatus,
Unique: false,
Indexer: &memdb.IntFieldIndex{Field: "Status"},
},
},
},
tableStepDefintionMatch: {
Name: tableStepDefintionMatch,
Indexes: map[string]*memdb.IndexSchema{
tableStepDefintionMatchIndexStepID: {
Name: tableStepDefintionMatchIndexStepID,
Unique: true,
Indexer: &memdb.StringFieldIndex{Field: "StepID"},
},
},
},
},
}
db, err := memdb.NewMemDB(&schema)
if err != nil {
panic(err)
}
return &Storage{db: db, testRunStartedLock: new(sync.Mutex)}
}
// MustInsertPickle will insert a pickle and it's steps,
// will panic on error.
func (s *Storage) MustInsertPickle(p *messages.Pickle) {
txn := s.db.Txn(writeMode)
if err := txn.Insert(tablePickle, p); err != nil {
panic(err)
}
for _, step := range p.Steps {
if err := txn.Insert(tablePickleStep, step); err != nil {
panic(err)
}
}
txn.Commit()
}
// MustGetPickle will retrieve a pickle by id and panic on error.
func (s *Storage) MustGetPickle(id string) *messages.Pickle {
v := s.mustFirst(tablePickle, tablePickleIndexID, id)
return v.(*messages.Pickle)
}
// MustGetPickles will retrieve pickles by URI and panic on error.
func (s *Storage) MustGetPickles(uri string) (ps []*messages.Pickle) {
it := s.mustGet(tablePickle, tablePickleIndexURI, uri)
for v := it.Next(); v != nil; v = it.Next() {
ps = append(ps, v.(*messages.Pickle))
}
return
}
// MustGetPickleStep will retrieve a pickle step and panic on error.
func (s *Storage) MustGetPickleStep(id string) *messages.PickleStep {
v := s.mustFirst(tablePickleStep, tablePickleStepIndexID, id)
return v.(*messages.PickleStep)
}
// MustInsertTestRunStarted will set the test run started event and panic on error.
func (s *Storage) MustInsertTestRunStarted(trs models.TestRunStarted) {
s.testRunStartedLock.Lock()
defer s.testRunStartedLock.Unlock()
s.testRunStarted = trs
}
// MustGetTestRunStarted will retrieve the test run started event and panic on error.
func (s *Storage) MustGetTestRunStarted() models.TestRunStarted {
s.testRunStartedLock.Lock()
defer s.testRunStartedLock.Unlock()
return s.testRunStarted
}
// MustInsertPickleResult will instert a pickle result and panic on error.
func (s *Storage) MustInsertPickleResult(pr models.PickleResult) {
s.mustInsert(tablePickleResult, pr)
}
// MustInsertPickleStepResult will insert a pickle step result and panic on error.
func (s *Storage) MustInsertPickleStepResult(psr models.PickleStepResult) {
s.mustInsert(tablePickleStepResult, psr)
}
// MustGetPickleResult will retrieve a pickle result by id and panic on error.
func (s *Storage) MustGetPickleResult(id string) models.PickleResult {
v := s.mustFirst(tablePickleResult, tablePickleResultIndexPickleID, id)
return v.(models.PickleResult)
}
// MustGetPickleResults will retrieve all pickle results and panic on error.
func (s *Storage) MustGetPickleResults() (prs []models.PickleResult) {
it := s.mustGet(tablePickleResult, tablePickleResultIndexPickleID)
for v := it.Next(); v != nil; v = it.Next() {
prs = append(prs, v.(models.PickleResult))
}
return prs
}
// MustGetPickleStepResult will retrieve a pickle strep result by id and panic on error.
func (s *Storage) MustGetPickleStepResult(id string) models.PickleStepResult {
v := s.mustFirst(tablePickleStepResult, tablePickleStepResultIndexPickleStepID, id)
return v.(models.PickleStepResult)
}
// MustGetPickleStepResultsByPickleID will retrieve pickle strep results by pickle id and panic on error.
func (s *Storage) MustGetPickleStepResultsByPickleID(pickleID string) (psrs []models.PickleStepResult) {
it := s.mustGet(tablePickleStepResult, tablePickleStepResultIndexPickleID, pickleID)
for v := it.Next(); v != nil; v = it.Next() {
psrs = append(psrs, v.(models.PickleStepResult))
}
sort.Sort(stepsSortByPickleStepID(psrs))
return psrs
}
// MustGetPickleStepResultsByStatus will retrieve pickle strep results by status and panic on error.
func (s *Storage) MustGetPickleStepResultsByStatus(status models.StepResultStatus) (psrs []models.PickleStepResult) {
it := s.mustGet(tablePickleStepResult, tablePickleStepResultIndexStatus, status)
for v := it.Next(); v != nil; v = it.Next() {
psrs = append(psrs, v.(models.PickleStepResult))
}
sort.Sort(stepsSortByPickleStepID(psrs))
return psrs
}
type stepsSortByPickleStepID []models.PickleStepResult
func (s stepsSortByPickleStepID) Len() int {
return len(s)
}
func (s stepsSortByPickleStepID) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
func (s stepsSortByPickleStepID) Less(i, j int) bool {
num1, err := strconv.Atoi(s[i].PickleStepID)
if err != nil {
return false
}
num2, err := strconv.Atoi(s[j].PickleStepID)
if err != nil {
return false
}
return num1 < num2
}
// MustInsertFeature will insert a feature and panic on error.
func (s *Storage) MustInsertFeature(f *models.Feature) {
s.mustInsert(tableFeature, f)
}
// MustGetFeature will retrieve a feature by URI and panic on error.
func (s *Storage) MustGetFeature(uri string) *models.Feature {
v := s.mustFirst(tableFeature, tableFeatureIndexURI, uri)
return v.(*models.Feature)
}
// MustGetFeatures will retrieve all features by and panic on error.
func (s *Storage) MustGetFeatures() (fs []*models.Feature) {
it := s.mustGet(tableFeature, tableFeatureIndexURI)
for v := it.Next(); v != nil; v = it.Next() {
fs = append(fs, v.(*models.Feature))
}
return
}
type stepDefinitionMatch struct {
StepID string
StepDefinition *models.StepDefinition
}
// MustInsertStepDefintionMatch will insert the matched StepDefintion for the step ID and panic on error.
func (s *Storage) MustInsertStepDefintionMatch(stepID string, match *models.StepDefinition) {
d := stepDefinitionMatch{
StepID: stepID,
StepDefinition: match,
}
s.mustInsert(tableStepDefintionMatch, d)
}
// MustGetStepDefintionMatch will retrieve the matched StepDefintion for the step ID and panic on error.
func (s *Storage) MustGetStepDefintionMatch(stepID string) *models.StepDefinition {
v := s.mustFirst(tableStepDefintionMatch, tableStepDefintionMatchIndexStepID, stepID)
return v.(stepDefinitionMatch).StepDefinition
}
func (s *Storage) mustInsert(table string, obj interface{}) {
txn := s.db.Txn(writeMode)
if err := txn.Insert(table, obj); err != nil {
panic(err)
}
txn.Commit()
}
func (s *Storage) mustFirst(table, index string, args ...interface{}) interface{} {
txn := s.db.Txn(readMode)
defer txn.Abort()
v, err := txn.First(table, index, args...)
if err != nil {
panic(err)
} else if v == nil {
err = fmt.Errorf("Couldn't find index: %q in table: %q with args: %+v", index, table, args)
panic(err)
}
return v
}
func (s *Storage) mustGet(table, index string, args ...interface{}) memdb.ResultIterator {
txn := s.db.Txn(readMode)
defer txn.Abort()
it, err := txn.Get(table, index, args...)
if err != nil {
panic(err)
}
return it
}