mirror of
https://github.com/LukeHagar/libopenapi.git
synced 2025-12-09 20:47:44 +00:00
Improve coverage. Simplify error handling.
This commit is contained in:
@@ -4,7 +4,6 @@
|
|||||||
package v2
|
package v2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
@@ -72,8 +71,7 @@ func (p *Paths) Build(_, root *yaml.Node, idx *index.SpecIndex) error {
|
|||||||
pathsMap := make(map[low.KeyReference[string]]low.ValueReference[*PathItem])
|
pathsMap := make(map[low.KeyReference[string]]low.ValueReference[*PathItem])
|
||||||
in := make(chan buildInput)
|
in := make(chan buildInput)
|
||||||
out := make(chan pathBuildResult)
|
out := make(chan pathBuildResult)
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
done := make(chan struct{})
|
||||||
defer cancel()
|
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
wg.Add(2) // input and output goroutines.
|
wg.Add(2) // input and output goroutines.
|
||||||
|
|
||||||
@@ -104,7 +102,7 @@ func (p *Paths) Build(_, root *yaml.Node, idx *index.SpecIndex) error {
|
|||||||
currentNode: currentNode,
|
currentNode: currentNode,
|
||||||
pathNode: pathNode,
|
pathNode: pathNode,
|
||||||
}:
|
}:
|
||||||
case <-ctx.Done():
|
case <-done:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -112,31 +110,25 @@ func (p *Paths) Build(_, root *yaml.Node, idx *index.SpecIndex) error {
|
|||||||
|
|
||||||
// TranslatePipeline output.
|
// TranslatePipeline output.
|
||||||
go func() {
|
go func() {
|
||||||
defer func() {
|
|
||||||
cancel()
|
|
||||||
wg.Done()
|
|
||||||
}()
|
|
||||||
for {
|
for {
|
||||||
select {
|
result, ok := <-out
|
||||||
case result, ok := <-out:
|
if !ok {
|
||||||
if !ok {
|
break
|
||||||
return
|
|
||||||
}
|
|
||||||
pathsMap[result.key] = result.value
|
|
||||||
case <-ctx.Done():
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
pathsMap[result.key] = result.value
|
||||||
}
|
}
|
||||||
|
close(done)
|
||||||
|
wg.Done()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
translateFunc := func(value buildInput) (retval pathBuildResult, _ error) {
|
translateFunc := func(value buildInput) (pathBuildResult, error) {
|
||||||
pNode := value.pathNode
|
pNode := value.pathNode
|
||||||
cNode := value.currentNode
|
cNode := value.currentNode
|
||||||
path := new(PathItem)
|
path := new(PathItem)
|
||||||
_ = low.BuildModel(pNode, path)
|
_ = low.BuildModel(pNode, path)
|
||||||
err := path.Build(cNode, pNode, idx)
|
err := path.Build(cNode, pNode, idx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return retval, err
|
return pathBuildResult{}, err
|
||||||
}
|
}
|
||||||
return pathBuildResult{
|
return pathBuildResult{
|
||||||
key: low.KeyReference[string]{
|
key: low.KeyReference[string]{
|
||||||
|
|||||||
@@ -4,11 +4,13 @@
|
|||||||
package v2
|
package v2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
"github.com/pb33f/libopenapi/datamodel/low"
|
"github.com/pb33f/libopenapi/datamodel/low"
|
||||||
"github.com/pb33f/libopenapi/index"
|
"github.com/pb33f/libopenapi/index"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"testing"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestPaths_Build(t *testing.T) {
|
func TestPaths_Build(t *testing.T) {
|
||||||
@@ -33,10 +35,10 @@ func TestPaths_Build(t *testing.T) {
|
|||||||
func TestPaths_FindPathAndKey(t *testing.T) {
|
func TestPaths_FindPathAndKey(t *testing.T) {
|
||||||
|
|
||||||
yml := `/no/sleep:
|
yml := `/no/sleep:
|
||||||
get:
|
get:
|
||||||
description: til brooklyn
|
description: til brooklyn
|
||||||
/no/pizza:
|
/no/pizza:
|
||||||
post:
|
post:
|
||||||
description: because i'm fat`
|
description: because i'm fat`
|
||||||
|
|
||||||
var idxNode yaml.Node
|
var idxNode yaml.Node
|
||||||
@@ -57,7 +59,7 @@ func TestPaths_Hash(t *testing.T) {
|
|||||||
|
|
||||||
yml := `/data/dog:
|
yml := `/data/dog:
|
||||||
get:
|
get:
|
||||||
description: does data kinda, ish.
|
description: does data kinda, ish.
|
||||||
/snow/flake:
|
/snow/flake:
|
||||||
get:
|
get:
|
||||||
description: does data
|
description: does data
|
||||||
@@ -80,7 +82,7 @@ x-milk: creamy`
|
|||||||
description: does data the best
|
description: does data the best
|
||||||
/data/dog:
|
/data/dog:
|
||||||
get:
|
get:
|
||||||
description: does data kinda, ish.
|
description: does data kinda, ish.
|
||||||
/snow/flake:
|
/snow/flake:
|
||||||
get:
|
get:
|
||||||
description: does data
|
description: does data
|
||||||
@@ -99,3 +101,28 @@ x-milk: creamy`
|
|||||||
assert.Len(t, n.GetExtensions(), 1)
|
assert.Len(t, n.GetExtensions(), 1)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test parse failure among many paths.
|
||||||
|
// This stresses `TranslatePipeline`'s error handling.
|
||||||
|
func TestPaths_Build_Fail_Many(t *testing.T) {
|
||||||
|
var yml string
|
||||||
|
for i := 0; i < 1000; i++ {
|
||||||
|
format := `"/fresh/code%d":
|
||||||
|
parameters:
|
||||||
|
$ref: break
|
||||||
|
`
|
||||||
|
yml += fmt.Sprintf(format, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
var idxNode yaml.Node
|
||||||
|
mErr := yaml.Unmarshal([]byte(yml), &idxNode)
|
||||||
|
assert.NoError(t, mErr)
|
||||||
|
idx := index.NewSpecIndex(&idxNode)
|
||||||
|
|
||||||
|
var n Paths
|
||||||
|
err := low.BuildModel(&idxNode, &n)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
err = n.Build(idxNode.Content[0], idx)
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
package v3
|
package v3
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
@@ -234,16 +233,18 @@ func extractComponentValues[T low.Buildable[N], N any](label string, root *yaml.
|
|||||||
return emptyResult, fmt.Errorf("node is array, cannot be used in components: line %d, column %d", nodeValue.Line, nodeValue.Column)
|
return emptyResult, fmt.Errorf("node is array, cannot be used in components: line %d, column %d", nodeValue.Line, nodeValue.Column)
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
defer cancel()
|
|
||||||
in := make(chan componentInput)
|
in := make(chan componentInput)
|
||||||
out := make(chan componentBuildResult[T])
|
out := make(chan componentBuildResult[T])
|
||||||
|
done := make(chan struct{})
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
wg.Add(2) // input and output goroutines.
|
wg.Add(2) // input and output goroutines.
|
||||||
|
|
||||||
// Send input.
|
// Send input.
|
||||||
go func() {
|
go func() {
|
||||||
defer wg.Done()
|
defer func() {
|
||||||
|
close(in)
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
var currentLabel *yaml.Node
|
var currentLabel *yaml.Node
|
||||||
for i, node := range nodeValue.Content {
|
for i, node := range nodeValue.Content {
|
||||||
// always ignore extensions
|
// always ignore extensions
|
||||||
@@ -261,11 +262,10 @@ func extractComponentValues[T low.Buildable[N], N any](label string, root *yaml.
|
|||||||
node: node,
|
node: node,
|
||||||
currentLabel: currentLabel,
|
currentLabel: currentLabel,
|
||||||
}:
|
}:
|
||||||
case <-ctx.Done():
|
case <-done:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
close(in)
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Collect output.
|
// Collect output.
|
||||||
@@ -273,7 +273,7 @@ func extractComponentValues[T low.Buildable[N], N any](label string, root *yaml.
|
|||||||
for result := range out {
|
for result := range out {
|
||||||
componentValues[result.key] = result.value
|
componentValues[result.key] = result.value
|
||||||
}
|
}
|
||||||
cancel()
|
close(done)
|
||||||
wg.Done()
|
wg.Done()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|||||||
@@ -4,11 +4,13 @@
|
|||||||
package v3
|
package v3
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
"github.com/pb33f/libopenapi/datamodel/low"
|
"github.com/pb33f/libopenapi/datamodel/low"
|
||||||
"github.com/pb33f/libopenapi/index"
|
"github.com/pb33f/libopenapi/index"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"testing"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var testComponentsYaml = `
|
var testComponentsYaml = `
|
||||||
@@ -38,7 +40,7 @@ var testComponentsYaml = `
|
|||||||
description: nine of many
|
description: nine of many
|
||||||
ten:
|
ten:
|
||||||
description: ten of many
|
description: ten of many
|
||||||
headers:
|
headers:
|
||||||
eleven:
|
eleven:
|
||||||
description: eleven of many
|
description: eleven of many
|
||||||
twelve:
|
twelve:
|
||||||
@@ -53,7 +55,7 @@ var testComponentsYaml = `
|
|||||||
description: fifteen of many
|
description: fifteen of many
|
||||||
sixteen:
|
sixteen:
|
||||||
description: sixteen of many
|
description: sixteen of many
|
||||||
callbacks:
|
callbacks:
|
||||||
seventeen:
|
seventeen:
|
||||||
'{reference}':
|
'{reference}':
|
||||||
post:
|
post:
|
||||||
@@ -124,7 +126,7 @@ func TestComponents_Build_Success_Skip(t *testing.T) {
|
|||||||
func TestComponents_Build_Fail(t *testing.T) {
|
func TestComponents_Build_Fail(t *testing.T) {
|
||||||
|
|
||||||
yml := `
|
yml := `
|
||||||
parameters:
|
parameters:
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/this is a problem.'`
|
$ref: '#/this is a problem.'`
|
||||||
|
|
||||||
@@ -164,10 +166,39 @@ func TestComponents_Build_ParameterFail(t *testing.T) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test parse failure among many parameters.
|
||||||
|
// This stresses `TranslatePipeline`'s error handling.
|
||||||
|
func TestComponents_Build_ParameterFail_Many(t *testing.T) {
|
||||||
|
yml := `
|
||||||
|
parameters:
|
||||||
|
`
|
||||||
|
|
||||||
|
for i := 0; i < 1000; i++ {
|
||||||
|
format := `
|
||||||
|
pizza%d:
|
||||||
|
schema:
|
||||||
|
$ref: '#/this is a problem.'
|
||||||
|
`
|
||||||
|
yml += fmt.Sprintf(format, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
var idxNode yaml.Node
|
||||||
|
mErr := yaml.Unmarshal([]byte(yml), &idxNode)
|
||||||
|
assert.NoError(t, mErr)
|
||||||
|
idx := index.NewSpecIndex(&idxNode)
|
||||||
|
|
||||||
|
var n Components
|
||||||
|
err := low.BuildModel(&idxNode, &n)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
err = n.Build(idxNode.Content[0], idx)
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
func TestComponents_Build_Fail_TypeFail(t *testing.T) {
|
func TestComponents_Build_Fail_TypeFail(t *testing.T) {
|
||||||
|
|
||||||
yml := `
|
yml := `
|
||||||
parameters:
|
parameters:
|
||||||
- schema:
|
- schema:
|
||||||
$ref: #/this is a problem.`
|
$ref: #/this is a problem.`
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
package v3
|
package v3
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
@@ -69,7 +68,7 @@ func (p *Paths) Build(_, root *yaml.Node, idx *index.SpecIndex) error {
|
|||||||
|
|
||||||
// Translate YAML nodes to pathsMap using `TranslatePipeline`.
|
// Translate YAML nodes to pathsMap using `TranslatePipeline`.
|
||||||
type buildResult struct {
|
type buildResult struct {
|
||||||
key low.KeyReference[string]
|
key low.KeyReference[string]
|
||||||
value low.ValueReference[*PathItem]
|
value low.ValueReference[*PathItem]
|
||||||
}
|
}
|
||||||
type buildInput struct {
|
type buildInput struct {
|
||||||
@@ -79,8 +78,7 @@ func (p *Paths) Build(_, root *yaml.Node, idx *index.SpecIndex) error {
|
|||||||
pathsMap := make(map[low.KeyReference[string]]low.ValueReference[*PathItem])
|
pathsMap := make(map[low.KeyReference[string]]low.ValueReference[*PathItem])
|
||||||
in := make(chan buildInput)
|
in := make(chan buildInput)
|
||||||
out := make(chan buildResult)
|
out := make(chan buildResult)
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
done := make(chan struct{})
|
||||||
defer cancel()
|
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
wg.Add(2) // input and output goroutines.
|
wg.Add(2) // input and output goroutines.
|
||||||
|
|
||||||
@@ -111,7 +109,7 @@ func (p *Paths) Build(_, root *yaml.Node, idx *index.SpecIndex) error {
|
|||||||
currentNode: currentNode,
|
currentNode: currentNode,
|
||||||
pathNode: pathNode,
|
pathNode: pathNode,
|
||||||
}:
|
}:
|
||||||
case <-ctx.Done():
|
case <-done:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -119,21 +117,15 @@ func (p *Paths) Build(_, root *yaml.Node, idx *index.SpecIndex) error {
|
|||||||
|
|
||||||
// TranslatePipeline output.
|
// TranslatePipeline output.
|
||||||
go func() {
|
go func() {
|
||||||
defer func() {
|
|
||||||
cancel()
|
|
||||||
wg.Done()
|
|
||||||
}()
|
|
||||||
for {
|
for {
|
||||||
select {
|
result, ok := <-out
|
||||||
case result, ok := <-out:
|
if !ok {
|
||||||
if !ok {
|
break
|
||||||
return
|
|
||||||
}
|
|
||||||
pathsMap[result.key] = result.value
|
|
||||||
case <-ctx.Done():
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
pathsMap[result.key] = result.value
|
||||||
}
|
}
|
||||||
|
close(done)
|
||||||
|
wg.Done()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
err := datamodel.TranslatePipeline[buildInput, buildResult](in, out,
|
err := datamodel.TranslatePipeline[buildInput, buildResult](in, out,
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
package v3
|
package v3
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/pb33f/libopenapi/datamodel/low"
|
"github.com/pb33f/libopenapi/datamodel/low"
|
||||||
@@ -21,15 +22,15 @@ func TestPaths_Build(t *testing.T) {
|
|||||||
post:
|
post:
|
||||||
description: post method
|
description: post method
|
||||||
put:
|
put:
|
||||||
description: put method
|
description: put method
|
||||||
delete:
|
delete:
|
||||||
description: delete method
|
description: delete method
|
||||||
options:
|
options:
|
||||||
description: options method
|
description: options method
|
||||||
patch:
|
patch:
|
||||||
description: patch method
|
description: patch method
|
||||||
head:
|
head:
|
||||||
description: head method
|
description: head method
|
||||||
trace:
|
trace:
|
||||||
description: trace method
|
description: trace method
|
||||||
servers:
|
servers:
|
||||||
@@ -128,7 +129,7 @@ func TestPaths_Build_FailRefDeadEnd(t *testing.T) {
|
|||||||
$ref: '#/nowhere'
|
$ref: '#/nowhere'
|
||||||
"/some/path":
|
"/some/path":
|
||||||
get:
|
get:
|
||||||
$ref: '#/no/path'
|
$ref: '#/no/path'
|
||||||
"/another/path":
|
"/another/path":
|
||||||
$ref: '#/~1some~1path'`
|
$ref: '#/~1some~1path'`
|
||||||
|
|
||||||
@@ -239,7 +240,7 @@ func TestPathItem_Build_GoodRef(t *testing.T) {
|
|||||||
get:
|
get:
|
||||||
$ref: '#/~1cakes/get'
|
$ref: '#/~1cakes/get'
|
||||||
"/cakes":
|
"/cakes":
|
||||||
description: cakes are awesome
|
description: cakes are awesome
|
||||||
get:
|
get:
|
||||||
description: get method from /cakes`
|
description: get method from /cakes`
|
||||||
|
|
||||||
@@ -269,7 +270,7 @@ func TestPathItem_Build_BadRef(t *testing.T) {
|
|||||||
get:
|
get:
|
||||||
$ref: '#/~1cakes/NotFound'
|
$ref: '#/~1cakes/NotFound'
|
||||||
"/cakes":
|
"/cakes":
|
||||||
description: cakes are awesome
|
description: cakes are awesome
|
||||||
get:
|
get:
|
||||||
description: get method from /cakes`
|
description: get method from /cakes`
|
||||||
|
|
||||||
@@ -478,3 +479,27 @@ x-france: french`
|
|||||||
assert.Nil(t, b)
|
assert.Nil(t, b)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test parse failure among many paths.
|
||||||
|
// This stresses `TranslatePipeline`'s error handling.
|
||||||
|
func TestPaths_Build_Fail_Many(t *testing.T) {
|
||||||
|
var yml string
|
||||||
|
for i := 0; i < 1000; i++ {
|
||||||
|
format := `"/fresh/code%d":
|
||||||
|
parameters:
|
||||||
|
$ref: break
|
||||||
|
`
|
||||||
|
yml += fmt.Sprintf(format, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
var idxNode yaml.Node
|
||||||
|
_ = yaml.Unmarshal([]byte(yml), &idxNode)
|
||||||
|
idx := index.NewSpecIndex(&idxNode)
|
||||||
|
|
||||||
|
var n Paths
|
||||||
|
err := low.BuildModel(&idxNode, &n)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
err = n.Build(idxNode.Content[0], idx)
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package datamodel_test
|
package datamodel_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
@@ -10,7 +9,6 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/pb33f/libopenapi/datamodel"
|
"github.com/pb33f/libopenapi/datamodel"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@@ -49,12 +47,29 @@ func TestTranslateSliceParallel(t *testing.T) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
err := datamodel.TranslateSliceParallel[int, string](sl, translateFunc, resultFunc)
|
err := datamodel.TranslateSliceParallel[int, string](sl, translateFunc, resultFunc)
|
||||||
time.Sleep(10 * time.Millisecond) // DEBUG
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, int64(mapSize), translateCounter)
|
assert.Equal(t, int64(mapSize), translateCounter)
|
||||||
assert.Equal(t, mapSize, resultCounter)
|
assert.Equal(t, mapSize, resultCounter)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("nil", func(t *testing.T) {
|
||||||
|
var sl []int
|
||||||
|
var translateCounter int64
|
||||||
|
translateFunc := func(_, value int) (string, error) {
|
||||||
|
atomic.AddInt64(&translateCounter, 1)
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
var resultCounter int
|
||||||
|
resultFunc := func(value string) error {
|
||||||
|
resultCounter++
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
err := datamodel.TranslateSliceParallel[int, string](sl, translateFunc, resultFunc)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Zero(t, translateCounter)
|
||||||
|
assert.Zero(t, resultCounter)
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("Error in translate", func(t *testing.T) {
|
t.Run("Error in translate", func(t *testing.T) {
|
||||||
var sl []int
|
var sl []int
|
||||||
for i := 0; i < mapSize; i++ {
|
for i := 0; i < mapSize; i++ {
|
||||||
@@ -188,6 +203,24 @@ func TestTranslateMapParallel(t *testing.T) {
|
|||||||
assert.Equal(t, expectedResults, results)
|
assert.Equal(t, expectedResults, results)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("nil", func(t *testing.T) {
|
||||||
|
var m map[string]int
|
||||||
|
var translateCounter int64
|
||||||
|
translateFunc := func(_ string, value int) (string, error) {
|
||||||
|
atomic.AddInt64(&translateCounter, 1)
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
var resultCounter int
|
||||||
|
resultFunc := func(value string) error {
|
||||||
|
resultCounter++
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
err := datamodel.TranslateMapParallel[string, int, string](m, translateFunc, resultFunc)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Zero(t, translateCounter)
|
||||||
|
assert.Zero(t, resultCounter)
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("Error in translate", func(t *testing.T) {
|
t.Run("Error in translate", func(t *testing.T) {
|
||||||
m := make(map[string]int)
|
m := make(map[string]int)
|
||||||
for i := 0; i < mapSize; i++ {
|
for i := 0; i < mapSize; i++ {
|
||||||
@@ -303,43 +336,39 @@ func TestTranslatePipeline(t *testing.T) {
|
|||||||
var inputErr error
|
var inputErr error
|
||||||
in := make(chan int)
|
in := make(chan int)
|
||||||
out := make(chan string)
|
out := make(chan string)
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
done := make(chan struct{})
|
||||||
defer cancel()
|
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
wg.Add(2) // input and output goroutines.
|
wg.Add(2) // input and output goroutines.
|
||||||
|
|
||||||
// Send input.
|
// Send input.
|
||||||
go func() {
|
go func() {
|
||||||
defer wg.Done()
|
defer func() {
|
||||||
|
close(in)
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
for i := 0; i < itemCount; i++ {
|
for i := 0; i < itemCount; i++ {
|
||||||
select {
|
select {
|
||||||
case in <- i:
|
case in <- i:
|
||||||
case <-ctx.Done():
|
case <-done:
|
||||||
inputErr = errors.New("Context canceled unexpectedly")
|
inputErr = errors.New("Exited unexpectedly")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
close(in)
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Collect output.
|
// Collect output.
|
||||||
var resultCounter int
|
var resultCounter int
|
||||||
go func() {
|
go func() {
|
||||||
defer func() {
|
|
||||||
cancel()
|
|
||||||
wg.Done()
|
|
||||||
}()
|
|
||||||
for {
|
for {
|
||||||
select {
|
result, ok := <-out
|
||||||
case result, ok := <-out:
|
if !ok {
|
||||||
if !ok {
|
break
|
||||||
return
|
|
||||||
}
|
|
||||||
assert.Equal(t, strconv.Itoa(resultCounter), result)
|
|
||||||
resultCounter++
|
|
||||||
case <-ctx.Done():
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
assert.Equal(t, strconv.Itoa(resultCounter), result)
|
||||||
|
resultCounter++
|
||||||
}
|
}
|
||||||
|
close(done)
|
||||||
|
wg.Done()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
err := datamodel.TranslatePipeline[int, string](in, out,
|
err := datamodel.TranslatePipeline[int, string](in, out,
|
||||||
@@ -356,41 +385,36 @@ func TestTranslatePipeline(t *testing.T) {
|
|||||||
t.Run("Error in translate", func(t *testing.T) {
|
t.Run("Error in translate", func(t *testing.T) {
|
||||||
in := make(chan int)
|
in := make(chan int)
|
||||||
out := make(chan string)
|
out := make(chan string)
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
done := make(chan struct{})
|
||||||
defer cancel()
|
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
wg.Add(2) // input and output goroutines.
|
wg.Add(2) // input and output goroutines.
|
||||||
|
|
||||||
// Send input.
|
// Send input.
|
||||||
go func() {
|
go func() {
|
||||||
defer wg.Done()
|
|
||||||
for i := 0; i < itemCount; i++ {
|
for i := 0; i < itemCount; i++ {
|
||||||
select {
|
select {
|
||||||
case in <- i:
|
case in <- i:
|
||||||
case <-ctx.Done():
|
case <-done:
|
||||||
// Context expected to cancel after the first translate.
|
// Expected to exit after the first translate.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
close(in)
|
close(in)
|
||||||
|
wg.Done()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Collect output.
|
// Collect output.
|
||||||
var resultCounter int
|
var resultCounter int
|
||||||
go func() {
|
go func() {
|
||||||
defer func() {
|
defer func() {
|
||||||
cancel()
|
close(done)
|
||||||
wg.Done()
|
wg.Done()
|
||||||
}()
|
}()
|
||||||
for {
|
for {
|
||||||
select {
|
_, ok := <-out
|
||||||
case _, ok := <-out:
|
if !ok {
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
resultCounter++
|
|
||||||
case <-ctx.Done():
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
resultCounter++
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@@ -408,8 +432,7 @@ func TestTranslatePipeline(t *testing.T) {
|
|||||||
var inputErr error
|
var inputErr error
|
||||||
in := make(chan int)
|
in := make(chan int)
|
||||||
out := make(chan string)
|
out := make(chan string)
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
done := make(chan struct{})
|
||||||
defer cancel()
|
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
wg.Add(2) // input and output goroutines.
|
wg.Add(2) // input and output goroutines.
|
||||||
|
|
||||||
@@ -419,8 +442,8 @@ func TestTranslatePipeline(t *testing.T) {
|
|||||||
for i := 0; i < itemCount; i++ {
|
for i := 0; i < itemCount; i++ {
|
||||||
select {
|
select {
|
||||||
case in <- i:
|
case in <- i:
|
||||||
case <-ctx.Done():
|
case <-done:
|
||||||
inputErr = errors.New("Context canceled unexpectedly")
|
inputErr = errors.New("Exited unexpectedly")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
close(in)
|
close(in)
|
||||||
@@ -429,21 +452,15 @@ func TestTranslatePipeline(t *testing.T) {
|
|||||||
// Collect output.
|
// Collect output.
|
||||||
var resultCounter int
|
var resultCounter int
|
||||||
go func() {
|
go func() {
|
||||||
defer func() {
|
|
||||||
cancel()
|
|
||||||
wg.Done()
|
|
||||||
}()
|
|
||||||
for {
|
for {
|
||||||
select {
|
_, ok := <-out
|
||||||
case _, ok := <-out:
|
if !ok {
|
||||||
if !ok {
|
break
|
||||||
return
|
|
||||||
}
|
|
||||||
resultCounter++
|
|
||||||
case <-ctx.Done():
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
resultCounter++
|
||||||
}
|
}
|
||||||
|
close(done)
|
||||||
|
wg.Done()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
err := datamodel.TranslatePipeline[int, string](in, out,
|
err := datamodel.TranslatePipeline[int, string](in, out,
|
||||||
|
|||||||
Reference in New Issue
Block a user