sqs-browse: started working on tests
This commit is contained in:
parent
7526c095ee
commit
cff059e160
37
.github/workflows/ci.yaml
vendored
Normal file
37
.github/workflows/ci.yaml
vendored
Normal file
|
@ -0,0 +1,37 @@
|
|||
name: ci
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- feature/*
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: amazon/dynamodb-local:latest
|
||||
ports:
|
||||
- 8000:8000
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.17
|
||||
- name: Configure
|
||||
run: |
|
||||
git config --global url."https://${{ secrets.GO_MODULES_TOKEN }}:x-oauth-basic@github.com/lmika".insteadOf "https://github.com/lmika"
|
||||
- name: Test
|
||||
run: |
|
||||
set -xue
|
||||
go get .
|
||||
go test ./...
|
||||
env:
|
||||
GOPRIVATE: "github:com/lmika/*"
|
30
go.mod
30
go.mod
|
@ -2,33 +2,39 @@ module github.com/lmika/awstools
|
|||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/aws/aws-sdk-go-v2 v1.15.0
|
||||
github.com/aws/aws-sdk-go-v2/config v1.13.1
|
||||
github.com/aws/aws-sdk-go-v2/service/dynamodb v1.15.0
|
||||
github.com/aws/aws-sdk-go-v2/service/sqs v1.16.0
|
||||
github.com/calyptia/go-bubble-table v0.1.0
|
||||
github.com/charmbracelet/bubbles v0.10.3
|
||||
github.com/charmbracelet/bubbletea v0.20.0
|
||||
github.com/charmbracelet/lipgloss v0.5.0
|
||||
github.com/lmika/events v0.0.0-20200906102219-a2269cd4394e
|
||||
github.com/lmika/gopkgs v0.0.0-20211210041137-0dc91e939890
|
||||
github.com/pkg/errors v0.9.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/atotto/clipboard v0.1.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.15.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.13.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.8.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.8.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.10.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.6 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/dynamodb v1.15.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.13.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.7.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.7.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sqs v1.16.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.9.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.14.0 // indirect
|
||||
github.com/aws/smithy-go v1.11.1 // indirect
|
||||
github.com/calyptia/go-bubble-table v0.1.0 // indirect
|
||||
github.com/charmbracelet/bubbles v0.10.3 // indirect
|
||||
github.com/charmbracelet/bubbletea v0.20.0 // indirect
|
||||
github.com/charmbracelet/lipgloss v0.5.0 // indirect
|
||||
github.com/containerd/console v1.0.3 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/juju/ansiterm v0.0.0-20210929141451-8b71cc96ebdc // indirect
|
||||
github.com/lmika/events v0.0.0-20200906102219-a2269cd4394e // indirect
|
||||
github.com/lmika/gopkgs v0.0.0-20211210041137-0dc91e939890 // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||
github.com/lunixbochs/vtclean v1.0.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
|
@ -36,8 +42,10 @@ require (
|
|||
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // indirect
|
||||
github.com/muesli/reflow v0.3.0 // indirect
|
||||
github.com/muesli/termenv v0.11.1-0.20220212125758-44cd13922739 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/stretchr/testify v1.7.1 // indirect
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
|
||||
)
|
||||
|
|
9
go.sum
9
go.sum
|
@ -8,6 +8,8 @@ github.com/aws/aws-sdk-go-v2/config v1.13.1 h1:yLv8bfNoT4r+UvUKQKqRtdnvuWGMK5a82
|
|||
github.com/aws/aws-sdk-go-v2/config v1.13.1/go.mod h1:Ba5Z4yL/UGbjQUzsiaN378YobhFo0MLfueXGiOsYtEs=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.8.0 h1:8Ow0WcyDesGNL0No11jcgb1JAtE+WtubqXjgxau+S0o=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.8.0/go.mod h1:gnMo58Vwx3Mu7hj1wpcG8DI0s57c9o42UQ6wgTQT5to=
|
||||
github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.8.0 h1:XxTy21xVUkoCZOSGwf+AW22v8aK3eEbYMaGGQ3MbKKk=
|
||||
github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.8.0/go.mod h1:6WkjzWenkrj3IgLPIPBBz4Qh99jNDF8L4Wj03vfMhAA=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.10.0 h1:NITDuUZO34mqtOwFWZiXo7yAHj7kf+XPE+EiKuCBNUI=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.10.0/go.mod h1:I6/fHT/fH460v09eg2gVrd8B/IqskhNdpcLH0WNO3QI=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.4 h1:CRiQJ4E2RhfDdqbie1ZYDo8QtIo75Mk7oTdJSfwJTMQ=
|
||||
|
@ -22,6 +24,8 @@ github.com/aws/aws-sdk-go-v2/internal/ini v1.3.5 h1:ixotxbfTCFpqbuwFv/RcZwyzhkxP
|
|||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.5/go.mod h1:R3sWUqPcfXSiF/LSFJhjyJmpg9uV6yP2yv3YZZjldVI=
|
||||
github.com/aws/aws-sdk-go-v2/service/dynamodb v1.15.0 h1:qnx+WyIH9/AD+wAxi05WCMNanO236ceqHg6hChCWs3M=
|
||||
github.com/aws/aws-sdk-go-v2/service/dynamodb v1.15.0/go.mod h1:+Kc1UmbE37ijaAsb3KogW6FR8z0myjX6VtdcCkQEK0k=
|
||||
github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.13.0 h1:s71pGCiLqqGRoUWtdJ2j4PazwEpZVwQc16na/4FfXdk=
|
||||
github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.13.0/go.mod h1:YGzTq/joAih4HRZZtMBWGP4bI8xVucOBQ9RvuanpclA=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.0 h1:uhb7moM7VjqIEpWzTpCvceLDSwrWpaleXm39OnVjuLE=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.0/go.mod h1:pA2St3Pu2Ldy6fBPY45Azoh1WBG4oS7eIKOd4XN7Meg=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.7.0 h1:6Bc0KHhAyxGe15JUHrK+Udw7KhE5LN+5HKZjQGo4yDI=
|
||||
|
@ -95,12 +99,15 @@ github.com/muesli/termenv v0.11.1-0.20220212125758-44cd13922739 h1:QANkGiGr39l1E
|
|||
github.com/muesli/termenv v0.11.1-0.20220212125758-44cd13922739/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
@ -115,3 +122,5 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
|
|||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
43
internal/dynamo-browse/models/attrutils.go
Normal file
43
internal/dynamo-browse/models/attrutils.go
Normal file
|
@ -0,0 +1,43 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
func compareScalarAttributes(x, y types.AttributeValue) (int, bool) {
|
||||
switch xVal := x.(type) {
|
||||
case *types.AttributeValueMemberS:
|
||||
if yVal, ok := y.(*types.AttributeValueMemberS); ok {
|
||||
return comparisonValue(xVal.Value == yVal.Value, xVal.Value < yVal.Value), true
|
||||
}
|
||||
case *types.AttributeValueMemberN:
|
||||
if yVal, ok := y.(*types.AttributeValueMemberN); ok {
|
||||
xNumVal, _, err := big.ParseFloat(xVal.Value, 10, 63, big.ToNearestEven)
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
yNumVal, _, err := big.ParseFloat(yVal.Value, 10, 63, big.ToNearestEven)
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
return xNumVal.Cmp(yNumVal), true
|
||||
}
|
||||
case *types.AttributeValueMemberBOOL:
|
||||
if yVal, ok := y.(*types.AttributeValueMemberBOOL); ok {
|
||||
return comparisonValue(xVal.Value == yVal.Value, !xVal.Value), true
|
||||
}
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
func comparisonValue(isEqual bool, isLess bool) int {
|
||||
if isEqual {
|
||||
return 0
|
||||
} else if isLess {
|
||||
return -1
|
||||
}
|
||||
return 1
|
||||
}
|
55
internal/dynamo-browse/models/sorted.go
Normal file
55
internal/dynamo-browse/models/sorted.go
Normal file
|
@ -0,0 +1,55 @@
|
|||
package models
|
||||
|
||||
import "sort"
|
||||
|
||||
// sortedItems is a collection of items that is sorted.
|
||||
// Items are sorted based on the PK, and SK in ascending order
|
||||
type sortedItems struct {
|
||||
pk, sk string
|
||||
items []Item
|
||||
}
|
||||
|
||||
// Sort sorts the items in place
|
||||
func Sort(items []Item, pk, sk string) {
|
||||
si := sortedItems{items: items, pk: pk, sk: sk}
|
||||
sort.Sort(&si)
|
||||
}
|
||||
|
||||
func (si *sortedItems) Len() int {
|
||||
return len(si.items)
|
||||
}
|
||||
|
||||
func (si *sortedItems) Less(i, j int) bool {
|
||||
// Compare primary keys
|
||||
pv1, pv2 := si.items[i][si.pk], si.items[j][si.pk]
|
||||
pc, ok := compareScalarAttributes(pv1, pv2)
|
||||
if !ok {
|
||||
return i < j
|
||||
}
|
||||
|
||||
if pc < 0 {
|
||||
return true
|
||||
} else if pc > 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
// Partition keys are equal, compare sort key
|
||||
sv1, sv2 := si.items[i][si.sk], si.items[j][si.sk]
|
||||
sc, ok := compareScalarAttributes(sv1, sv2)
|
||||
if !ok {
|
||||
return i < j
|
||||
}
|
||||
|
||||
if sc < 0 {
|
||||
return true
|
||||
} else if sc > 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
// This should never happen, but just in case
|
||||
return i < j
|
||||
}
|
||||
|
||||
func (si *sortedItems) Swap(i, j int) {
|
||||
si.items[j], si.items[i] = si.items[i], si.items[j]
|
||||
}
|
103
internal/dynamo-browse/models/sorted_test.go
Normal file
103
internal/dynamo-browse/models/sorted_test.go
Normal file
|
@ -0,0 +1,103 @@
|
|||
package models_test
|
||||
|
||||
import (
|
||||
"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
|
||||
"github.com/lmika/awstools/internal/dynamo-browse/models"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSort(t *testing.T) {
|
||||
t.Run("pk and sk are both strings", func(t *testing.T) {
|
||||
items := make([]models.Item, len(testStringData))
|
||||
copy(items, testStringData)
|
||||
|
||||
models.Sort(items, "pk", "sk")
|
||||
|
||||
assert.Equal(t, items[0], testStringData[1])
|
||||
assert.Equal(t, items[1], testStringData[2])
|
||||
assert.Equal(t, items[2], testStringData[0])
|
||||
})
|
||||
|
||||
t.Run("pk and sk are both numbers", func(t *testing.T) {
|
||||
items := make([]models.Item, len(testNumberData))
|
||||
copy(items, testNumberData)
|
||||
|
||||
models.Sort(items, "pk", "sk")
|
||||
|
||||
assert.Equal(t, items[0], testNumberData[2])
|
||||
assert.Equal(t, items[1], testNumberData[1])
|
||||
assert.Equal(t, items[2], testNumberData[0])
|
||||
})
|
||||
|
||||
t.Run("pk and sk are both bools", func(t *testing.T) {
|
||||
items := make([]models.Item, len(testBoolData))
|
||||
copy(items, testBoolData)
|
||||
|
||||
models.Sort(items, "pk", "sk")
|
||||
|
||||
assert.Equal(t, items[0], testBoolData[2])
|
||||
assert.Equal(t, items[1], testBoolData[1])
|
||||
assert.Equal(t, items[2], testBoolData[0])
|
||||
})
|
||||
}
|
||||
|
||||
var testStringData = []models.Item{
|
||||
{
|
||||
"pk": &types.AttributeValueMemberS{Value: "bbb"},
|
||||
"sk": &types.AttributeValueMemberS{Value: "131"},
|
||||
"beta": &types.AttributeValueMemberN{Value: "2468"},
|
||||
"gamma": &types.AttributeValueMemberS{Value: "foobar"},
|
||||
},
|
||||
{
|
||||
"pk": &types.AttributeValueMemberS{Value: "abc"},
|
||||
"sk": &types.AttributeValueMemberS{Value: "111"},
|
||||
"alpha": &types.AttributeValueMemberS{Value: "This is some value"},
|
||||
},
|
||||
{
|
||||
"pk": &types.AttributeValueMemberS{Value: "abc"},
|
||||
"sk": &types.AttributeValueMemberS{Value: "222"},
|
||||
"alpha": &types.AttributeValueMemberS{Value: "This is another some value"},
|
||||
"beta": &types.AttributeValueMemberN{Value: "2468"},
|
||||
},
|
||||
}
|
||||
|
||||
var testNumberData = []models.Item{
|
||||
{
|
||||
"pk": &types.AttributeValueMemberN{Value: "1141"},
|
||||
"sk": &types.AttributeValueMemberN{Value: "1111"},
|
||||
"beta": &types.AttributeValueMemberN{Value: "2468"},
|
||||
"gamma": &types.AttributeValueMemberS{Value: "foobar"},
|
||||
},
|
||||
{
|
||||
"pk": &types.AttributeValueMemberN{Value: "1141"},
|
||||
"sk": &types.AttributeValueMemberN{Value: "111.5"},
|
||||
"alpha": &types.AttributeValueMemberS{Value: "This is some value"},
|
||||
},
|
||||
{
|
||||
"pk": &types.AttributeValueMemberN{Value: "5"},
|
||||
"sk": &types.AttributeValueMemberN{Value: "222"},
|
||||
"alpha": &types.AttributeValueMemberS{Value: "This is another some value"},
|
||||
"beta": &types.AttributeValueMemberN{Value: "2468"},
|
||||
},
|
||||
}
|
||||
|
||||
var testBoolData = []models.Item{
|
||||
{
|
||||
"pk": &types.AttributeValueMemberBOOL{Value: true},
|
||||
"sk": &types.AttributeValueMemberBOOL{Value: true},
|
||||
"beta": &types.AttributeValueMemberN{Value: "2468"},
|
||||
"gamma": &types.AttributeValueMemberS{Value: "foobar"},
|
||||
},
|
||||
{
|
||||
"pk": &types.AttributeValueMemberBOOL{Value: true},
|
||||
"sk": &types.AttributeValueMemberBOOL{Value: false},
|
||||
"alpha": &types.AttributeValueMemberS{Value: "This is some value"},
|
||||
},
|
||||
{
|
||||
"pk": &types.AttributeValueMemberBOOL{Value: false},
|
||||
"sk": &types.AttributeValueMemberBOOL{Value: false},
|
||||
"alpha": &types.AttributeValueMemberS{Value: "This is another some value"},
|
||||
"beta": &types.AttributeValueMemberN{Value: "2468"},
|
||||
},
|
||||
}
|
113
internal/dynamo-browse/providers/dynamo/provider_test.go
Normal file
113
internal/dynamo-browse/providers/dynamo/provider_test.go
Normal file
|
@ -0,0 +1,113 @@
|
|||
package dynamo_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
|
||||
"github.com/lmika/awstools/internal/dynamo-browse/providers/dynamo"
|
||||
"github.com/lmika/awstools/test/testdynamo"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestProvider_ScanItems(t *testing.T) {
|
||||
tableName := "test-table"
|
||||
|
||||
client := testdynamo.SetupTestTable(t, tableName, testData)
|
||||
provider := dynamo.NewProvider(client)
|
||||
|
||||
t.Run("should return scanned items from the table", func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
items, err := provider.ScanItems(ctx, tableName)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, items, 3)
|
||||
|
||||
assert.Contains(t, items, testdynamo.TestRecordAsItem(t, testData[0]))
|
||||
assert.Contains(t, items, testdynamo.TestRecordAsItem(t, testData[1]))
|
||||
assert.Contains(t, items, testdynamo.TestRecordAsItem(t, testData[2]))
|
||||
})
|
||||
|
||||
t.Run("should return error if table name does not exist", func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
items, err := provider.ScanItems(ctx, "does-not-exist")
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, items)
|
||||
})
|
||||
}
|
||||
|
||||
func TestProvider_DeleteItem(t *testing.T) {
|
||||
tableName := "test-table"
|
||||
|
||||
t.Run("should delete item if exists in table", func(t *testing.T) {
|
||||
client := testdynamo.SetupTestTable(t, tableName, testData)
|
||||
provider := dynamo.NewProvider(client)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
err := provider.DeleteItem(ctx, tableName, map[string]types.AttributeValue{
|
||||
"pk": &types.AttributeValueMemberS{Value: "abc"},
|
||||
"sk": &types.AttributeValueMemberS{Value: "222"},
|
||||
})
|
||||
|
||||
items, err := provider.ScanItems(ctx, tableName)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, items, 2)
|
||||
|
||||
assert.Contains(t, items, testdynamo.TestRecordAsItem(t, testData[0]))
|
||||
assert.Contains(t, items, testdynamo.TestRecordAsItem(t, testData[2]))
|
||||
assert.NotContains(t, items, testdynamo.TestRecordAsItem(t, testData[1]))
|
||||
|
||||
})
|
||||
|
||||
t.Run("should do nothing if key does not exist", func(t *testing.T) {
|
||||
client := testdynamo.SetupTestTable(t, tableName, testData)
|
||||
provider := dynamo.NewProvider(client)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
err := provider.DeleteItem(ctx, tableName, map[string]types.AttributeValue{
|
||||
"pk": &types.AttributeValueMemberS{Value: "zyx"},
|
||||
"sk": &types.AttributeValueMemberS{Value: "999"},
|
||||
})
|
||||
|
||||
items, err := provider.ScanItems(ctx, tableName)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, items, 3)
|
||||
|
||||
assert.Contains(t, items, testdynamo.TestRecordAsItem(t, testData[0]))
|
||||
assert.Contains(t, items, testdynamo.TestRecordAsItem(t, testData[1]))
|
||||
assert.Contains(t, items, testdynamo.TestRecordAsItem(t, testData[2]))
|
||||
})
|
||||
|
||||
t.Run("should return error if table name does not exist", func(t *testing.T) {
|
||||
client := testdynamo.SetupTestTable(t, tableName, testData)
|
||||
provider := dynamo.NewProvider(client)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
items, err := provider.ScanItems(ctx, "does-not-exist")
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, items)
|
||||
})
|
||||
}
|
||||
|
||||
var testData = testdynamo.TestData{
|
||||
{
|
||||
"pk": "abc",
|
||||
"sk": "111",
|
||||
"alpha": "This is some value",
|
||||
},
|
||||
{
|
||||
"pk": "abc",
|
||||
"sk": "222",
|
||||
"alpha": "This is another some value",
|
||||
"beta": 1231,
|
||||
},
|
||||
{
|
||||
"pk": "bbb",
|
||||
"sk": "131",
|
||||
"beta": 2468,
|
||||
"gamma": "foobar",
|
||||
},
|
||||
}
|
|
@ -24,16 +24,18 @@ func (s *Service) Scan(ctx context.Context, table string) (*models.ResultSet, er
|
|||
return nil, errors.Wrapf(err, "unable to scan table %v", table)
|
||||
}
|
||||
|
||||
// Get the columns
|
||||
// TODO: need to get PKs and SKs from table
|
||||
pk, sk := "pk", "sk"
|
||||
|
||||
// Get the columns
|
||||
seenColumns := make(map[string]int)
|
||||
seenColumns["pk"] = 0
|
||||
seenColumns["sk"] = 1
|
||||
seenColumns[pk] = 0
|
||||
seenColumns[sk] = 1
|
||||
|
||||
for _, result := range results {
|
||||
for k := range result {
|
||||
if _, isSeen := seenColumns[k]; !isSeen {
|
||||
seenColumns[k] = len(seenColumns)
|
||||
seenColumns[k] = 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -43,12 +45,17 @@ func (s *Service) Scan(ctx context.Context, table string) (*models.ResultSet, er
|
|||
columns = append(columns, k)
|
||||
}
|
||||
sort.Slice(columns, func(i, j int) bool {
|
||||
if seenColumns[columns[i]] == seenColumns[columns[j]] {
|
||||
return columns[i] < columns[j]
|
||||
}
|
||||
return seenColumns[columns[i]] < seenColumns[columns[j]]
|
||||
})
|
||||
|
||||
models.Sort(results, pk, sk)
|
||||
|
||||
return &models.ResultSet{
|
||||
Columns: columns,
|
||||
Items: results,
|
||||
Items: results,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
51
internal/dynamo-browse/services/tables/service_test.go
Normal file
51
internal/dynamo-browse/services/tables/service_test.go
Normal file
|
@ -0,0 +1,51 @@
|
|||
package tables_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/lmika/awstools/internal/dynamo-browse/providers/dynamo"
|
||||
"github.com/lmika/awstools/internal/dynamo-browse/services/tables"
|
||||
"github.com/lmika/awstools/test/testdynamo"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestService_Scan(t *testing.T) {
|
||||
tableName := "test-table"
|
||||
|
||||
client := testdynamo.SetupTestTable(t, tableName, testData)
|
||||
provider := dynamo.NewProvider(client)
|
||||
|
||||
t.Run("return all columns and fields in sorted order", func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
service := tables.NewService(provider)
|
||||
rs, err := service.Scan(ctx, tableName)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Hash first, then range, then columns in alphabetic order
|
||||
assert.Equal(t, rs.Columns, []string{"pk", "sk", "alpha", "beta", "gamma"})
|
||||
assert.Equal(t, rs.Items[0], testdynamo.TestRecordAsItem(t, testData[1]))
|
||||
assert.Equal(t, rs.Items[1], testdynamo.TestRecordAsItem(t, testData[0]))
|
||||
assert.Equal(t, rs.Items[2], testdynamo.TestRecordAsItem(t, testData[2]))
|
||||
})
|
||||
}
|
||||
|
||||
var testData = testdynamo.TestData{
|
||||
{
|
||||
"pk": "abc",
|
||||
"sk": "222",
|
||||
"alpha": "This is another some value",
|
||||
"beta": 1231,
|
||||
},
|
||||
{
|
||||
"pk": "abc",
|
||||
"sk": "111",
|
||||
"alpha": "This is some value",
|
||||
},
|
||||
{
|
||||
"pk": "bbb",
|
||||
"sk": "131",
|
||||
"beta": 2468,
|
||||
"gamma": "foobar",
|
||||
},
|
||||
}
|
59
test/testdynamo/client.go
Normal file
59
test/testdynamo/client.go
Normal file
|
@ -0,0 +1,59 @@
|
|||
package testdynamo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
"github.com/aws/aws-sdk-go-v2/config"
|
||||
"github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue"
|
||||
"github.com/aws/aws-sdk-go-v2/service/dynamodb"
|
||||
"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type TestData []map[string]interface{}
|
||||
|
||||
func SetupTestTable(t *testing.T, tableName string, testData TestData) *dynamodb.Client {
|
||||
t.Helper()
|
||||
ctx := context.Background()
|
||||
|
||||
cfg, err := config.LoadDefaultConfig(ctx)
|
||||
assert.NoError(t, err)
|
||||
|
||||
dynamoClient := dynamodb.NewFromConfig(cfg,
|
||||
dynamodb.WithEndpointResolver(dynamodb.EndpointResolverFromURL("http://localhost:8000")))
|
||||
|
||||
dynamoClient.DeleteTable(ctx, &dynamodb.DeleteTableInput{
|
||||
TableName: aws.String(tableName),
|
||||
})
|
||||
|
||||
_, err = dynamoClient.CreateTable(ctx, &dynamodb.CreateTableInput{
|
||||
TableName: aws.String(tableName),
|
||||
KeySchema: []types.KeySchemaElement{
|
||||
{AttributeName: aws.String("pk"), KeyType: types.KeyTypeHash},
|
||||
{AttributeName: aws.String("sk"), KeyType: types.KeyTypeRange},
|
||||
},
|
||||
AttributeDefinitions: []types.AttributeDefinition{
|
||||
{AttributeName: aws.String("pk"), AttributeType: types.ScalarAttributeTypeS},
|
||||
{AttributeName: aws.String("sk"), AttributeType: types.ScalarAttributeTypeS},
|
||||
},
|
||||
ProvisionedThroughput: &types.ProvisionedThroughput{
|
||||
ReadCapacityUnits: aws.Int64(100),
|
||||
WriteCapacityUnits: aws.Int64(100),
|
||||
},
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
for _, item := range testData {
|
||||
m, err := attributevalue.MarshalMap(item)
|
||||
assert.NoError(t, err)
|
||||
|
||||
_, err = dynamoClient.PutItem(ctx, &dynamodb.PutItemInput{
|
||||
TableName: aws.String(tableName),
|
||||
Item: m,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
return dynamoClient
|
||||
}
|
15
test/testdynamo/helpers.go
Normal file
15
test/testdynamo/helpers.go
Normal file
|
@ -0,0 +1,15 @@
|
|||
package testdynamo
|
||||
|
||||
import (
|
||||
"github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue"
|
||||
"github.com/lmika/awstools/internal/dynamo-browse/models"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRecordAsItem(t *testing.T, item map[string]interface{}) models.Item {
|
||||
m, err := attributevalue.MarshalMap(item)
|
||||
assert.NoError(t, err)
|
||||
|
||||
return models.Item(m)
|
||||
}
|
Loading…
Reference in a new issue