421 lines
9.9 KiB
Go
421 lines
9.9 KiB
Go
// Copyright (c) Mainflux
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package twins_test
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"testing"
|
|
|
|
"github.com/mainflux/mainflux/twins"
|
|
"github.com/mainflux/mainflux/twins/mocks"
|
|
"github.com/mainflux/senml"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
const (
|
|
twinName = "name"
|
|
wrongID = ""
|
|
token = "token"
|
|
wrongToken = "wrong-token"
|
|
email = "user@example.com"
|
|
natsURL = "nats://localhost:4222"
|
|
numRecs = 100
|
|
)
|
|
|
|
var (
|
|
subtopics = []string{"engine", "chassis", "wheel_2"}
|
|
channels = []string{"01ec3c3e-0e66-4e69-9751-a0545b44e08f", "48061e4f-7c23-4f5c-9012-0f9b7cd9d18d", "5b2180e4-e96b-4469-9dc1-b6745078d0b6"}
|
|
)
|
|
|
|
func TestAddTwin(t *testing.T) {
|
|
svc := mocks.NewService(map[string]string{token: email})
|
|
twin := twins.Twin{}
|
|
def := twins.Definition{}
|
|
|
|
cases := []struct {
|
|
desc string
|
|
twin twins.Twin
|
|
token string
|
|
err error
|
|
}{
|
|
{
|
|
desc: "add new twin",
|
|
twin: twin,
|
|
token: token,
|
|
err: nil,
|
|
},
|
|
{
|
|
desc: "add twin with wrong credentials",
|
|
twin: twin,
|
|
token: wrongToken,
|
|
err: twins.ErrUnauthorizedAccess,
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
_, err := svc.AddTwin(context.Background(), tc.token, tc.twin, def)
|
|
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
|
|
}
|
|
}
|
|
|
|
func TestUpdateTwin(t *testing.T) {
|
|
svc := mocks.NewService(map[string]string{token: email})
|
|
twin := twins.Twin{}
|
|
other := twins.Twin{}
|
|
def := twins.Definition{}
|
|
|
|
other.ID = wrongID
|
|
saved, err := svc.AddTwin(context.Background(), token, twin, def)
|
|
require.Nil(t, err, fmt.Sprintf("unexpected error: %s\n", err))
|
|
|
|
saved.Name = twinName
|
|
|
|
cases := []struct {
|
|
desc string
|
|
twin twins.Twin
|
|
token string
|
|
err error
|
|
}{
|
|
{
|
|
desc: "update existing twin",
|
|
twin: saved,
|
|
token: token,
|
|
err: nil,
|
|
},
|
|
{
|
|
desc: "update twin with wrong credentials",
|
|
twin: saved,
|
|
token: wrongToken,
|
|
err: twins.ErrUnauthorizedAccess,
|
|
},
|
|
{
|
|
desc: "update non-existing twin",
|
|
twin: other,
|
|
token: token,
|
|
err: twins.ErrNotFound,
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
err := svc.UpdateTwin(context.Background(), tc.token, tc.twin, def)
|
|
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
|
|
}
|
|
}
|
|
|
|
func TestViewTwin(t *testing.T) {
|
|
svc := mocks.NewService(map[string]string{token: email})
|
|
twin := twins.Twin{}
|
|
def := twins.Definition{}
|
|
saved, err := svc.AddTwin(context.Background(), token, twin, def)
|
|
require.Nil(t, err, fmt.Sprintf("unexpected error: %s\n", err))
|
|
|
|
cases := map[string]struct {
|
|
id string
|
|
token string
|
|
err error
|
|
}{
|
|
"view existing twin": {
|
|
id: saved.ID,
|
|
token: token,
|
|
err: nil,
|
|
},
|
|
"view twin with wrong credentials": {
|
|
id: saved.ID,
|
|
token: wrongToken,
|
|
err: twins.ErrUnauthorizedAccess,
|
|
},
|
|
"view non-existing twin": {
|
|
id: wrongID,
|
|
token: token,
|
|
err: twins.ErrNotFound,
|
|
},
|
|
}
|
|
|
|
for desc, tc := range cases {
|
|
_, err := svc.ViewTwin(context.Background(), tc.token, tc.id)
|
|
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected %s got %s\n", desc, tc.err, err))
|
|
}
|
|
}
|
|
|
|
func TestListTwins(t *testing.T) {
|
|
svc := mocks.NewService(map[string]string{token: email})
|
|
twin := twins.Twin{Name: twinName, Owner: email}
|
|
def := twins.Definition{}
|
|
m := make(map[string]interface{})
|
|
m["serial"] = "123456"
|
|
twin.Metadata = m
|
|
|
|
n := uint64(10)
|
|
for i := uint64(0); i < n; i++ {
|
|
svc.AddTwin(context.Background(), token, twin, def)
|
|
}
|
|
|
|
cases := map[string]struct {
|
|
token string
|
|
offset uint64
|
|
limit uint64
|
|
size uint64
|
|
metadata map[string]interface{}
|
|
err error
|
|
}{
|
|
"list all twins": {
|
|
token: token,
|
|
offset: 0,
|
|
limit: n,
|
|
size: n,
|
|
err: nil,
|
|
},
|
|
"list with zero limit": {
|
|
token: token,
|
|
limit: 0,
|
|
offset: 0,
|
|
size: 0,
|
|
err: nil,
|
|
},
|
|
"list with offset and limit": {
|
|
token: token,
|
|
offset: 8,
|
|
limit: 5,
|
|
size: 2,
|
|
err: nil,
|
|
},
|
|
"list with wrong credentials": {
|
|
token: wrongToken,
|
|
limit: 0,
|
|
offset: n,
|
|
err: twins.ErrUnauthorizedAccess,
|
|
},
|
|
}
|
|
|
|
for desc, tc := range cases {
|
|
page, err := svc.ListTwins(context.Background(), tc.token, tc.offset, tc.limit, twinName, tc.metadata)
|
|
size := uint64(len(page.Twins))
|
|
assert.Equal(t, tc.size, size, fmt.Sprintf("%s: expected %d got %d\n", desc, tc.size, size))
|
|
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected %s got %s\n", desc, tc.err, err))
|
|
}
|
|
}
|
|
|
|
func TestRemoveTwin(t *testing.T) {
|
|
svc := mocks.NewService(map[string]string{token: email})
|
|
twin := twins.Twin{}
|
|
def := twins.Definition{}
|
|
saved, err := svc.AddTwin(context.Background(), token, twin, def)
|
|
require.Nil(t, err, fmt.Sprintf("unexpected error: %s\n", err))
|
|
|
|
cases := []struct {
|
|
desc string
|
|
id string
|
|
token string
|
|
err error
|
|
}{
|
|
{
|
|
desc: "remove twin with wrong credentials",
|
|
id: saved.ID,
|
|
token: wrongToken,
|
|
err: twins.ErrUnauthorizedAccess,
|
|
},
|
|
{
|
|
desc: "remove existing twin",
|
|
id: saved.ID,
|
|
token: token,
|
|
err: nil,
|
|
},
|
|
{
|
|
desc: "remove removed twin",
|
|
id: saved.ID,
|
|
token: token,
|
|
err: nil,
|
|
},
|
|
{
|
|
desc: "remove non-existing twin",
|
|
id: wrongID,
|
|
token: token,
|
|
err: nil,
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
err := svc.RemoveTwin(context.Background(), tc.token, tc.id)
|
|
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
|
|
}
|
|
}
|
|
|
|
func TestSaveStates(t *testing.T) {
|
|
svc := mocks.NewService(map[string]string{token: email})
|
|
|
|
twin := twins.Twin{Owner: email}
|
|
def := mocks.CreateDefinition(channels[0:2], subtopics[0:2])
|
|
attr := def.Attributes[0]
|
|
attrSansTwin := mocks.CreateDefinition(channels[2:3], subtopics[2:3]).Attributes[0]
|
|
tw, err := svc.AddTwin(context.Background(), token, twin, def)
|
|
require.Nil(t, err, fmt.Sprintf("unexpected error: %s", err))
|
|
|
|
defWildcard := mocks.CreateDefinition(channels[0:2], []string{twins.SubtopicWildcard, twins.SubtopicWildcard})
|
|
twWildcard, err := svc.AddTwin(context.Background(), token, twin, defWildcard)
|
|
require.Nil(t, err, fmt.Sprintf("unexpected error: %s", err))
|
|
|
|
var recs = make([]senml.Record, numRecs)
|
|
mocks.CreateSenML(numRecs, recs)
|
|
|
|
var ttlAdded uint64
|
|
|
|
cases := []struct {
|
|
desc string
|
|
recs []senml.Record
|
|
attr twins.Attribute
|
|
size uint64
|
|
err error
|
|
}{
|
|
{
|
|
desc: "add 100 states",
|
|
recs: recs,
|
|
attr: attr,
|
|
size: numRecs,
|
|
err: nil,
|
|
},
|
|
{
|
|
desc: "add 20 states",
|
|
recs: recs[10:30],
|
|
attr: attr,
|
|
size: 20,
|
|
err: nil,
|
|
},
|
|
{
|
|
desc: "add 20 states for atttribute without twin",
|
|
recs: recs[30:50],
|
|
size: 0,
|
|
attr: attrSansTwin,
|
|
err: twins.ErrNotFound,
|
|
},
|
|
{
|
|
desc: "use empty senml record",
|
|
recs: []senml.Record{},
|
|
attr: attr,
|
|
size: 0,
|
|
err: nil,
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
message, err := mocks.CreateMessage(tc.attr, tc.recs)
|
|
require.Nil(t, err, fmt.Sprintf("unexpected error: %s", err))
|
|
|
|
err = svc.SaveStates(message)
|
|
require.Nil(t, err, fmt.Sprintf("unexpected error: %s", err))
|
|
|
|
ttlAdded += tc.size
|
|
page, err := svc.ListStates(context.TODO(), token, 0, 10, tw.ID)
|
|
require.Nil(t, err, fmt.Sprintf("unexpected error: %s", err))
|
|
assert.Equal(t, ttlAdded, page.Total, fmt.Sprintf("%s: expected %d total got %d total\n", tc.desc, ttlAdded, page.Total))
|
|
|
|
page, err = svc.ListStates(context.TODO(), token, 0, 10, twWildcard.ID)
|
|
require.Nil(t, err, fmt.Sprintf("unexpected error: %s", err))
|
|
assert.Equal(t, ttlAdded, page.Total, fmt.Sprintf("%s: expected %d total got %d total\n", tc.desc, ttlAdded, page.Total))
|
|
}
|
|
}
|
|
|
|
func TestListStates(t *testing.T) {
|
|
svc := mocks.NewService(map[string]string{token: email})
|
|
|
|
twin := twins.Twin{Owner: email}
|
|
def := mocks.CreateDefinition(channels[0:2], subtopics[0:2])
|
|
attr := def.Attributes[0]
|
|
tw, err := svc.AddTwin(context.Background(), token, twin, def)
|
|
require.Nil(t, err, fmt.Sprintf("unexpected error: %s", err))
|
|
|
|
tw2, err := svc.AddTwin(context.Background(), token,
|
|
twins.Twin{Owner: email},
|
|
mocks.CreateDefinition(channels[2:3], subtopics[2:3]))
|
|
require.Nil(t, err, fmt.Sprintf("unexpected error: %s", err))
|
|
|
|
var recs = make([]senml.Record, numRecs)
|
|
mocks.CreateSenML(numRecs, recs)
|
|
message, err := mocks.CreateMessage(attr, recs)
|
|
require.Nil(t, err, fmt.Sprintf("unexpected error: %s", err))
|
|
err = svc.SaveStates(message)
|
|
require.Nil(t, err, fmt.Sprintf("unexpected error: %s", err))
|
|
|
|
cases := []struct {
|
|
desc string
|
|
id string
|
|
token string
|
|
offset uint64
|
|
limit uint64
|
|
size int
|
|
err error
|
|
}{
|
|
{
|
|
desc: "get a list of first 10 states",
|
|
id: tw.ID,
|
|
token: token,
|
|
offset: 0,
|
|
limit: 10,
|
|
size: 10,
|
|
err: nil,
|
|
},
|
|
{
|
|
desc: "get a list of last 10 states",
|
|
id: tw.ID,
|
|
token: token,
|
|
offset: numRecs - 10,
|
|
limit: numRecs,
|
|
size: 10,
|
|
err: nil,
|
|
},
|
|
{
|
|
desc: "get a list of last 10 states with limit > numRecs",
|
|
id: tw.ID,
|
|
token: token,
|
|
offset: numRecs - 10,
|
|
limit: numRecs + 10,
|
|
size: 10,
|
|
err: nil,
|
|
},
|
|
{
|
|
desc: "get a list of first 10 states with offset == numRecs",
|
|
id: tw.ID,
|
|
token: token,
|
|
offset: numRecs,
|
|
limit: numRecs + 10,
|
|
size: 0,
|
|
err: nil,
|
|
},
|
|
{
|
|
desc: "get a list with wrong user token",
|
|
id: tw.ID,
|
|
token: wrongToken,
|
|
offset: 0,
|
|
limit: 10,
|
|
size: 0,
|
|
err: twins.ErrUnauthorizedAccess,
|
|
},
|
|
{
|
|
desc: "get a list with id of non-existent twin",
|
|
id: "1234567890",
|
|
token: token,
|
|
offset: 0,
|
|
limit: 10,
|
|
size: 0,
|
|
err: nil,
|
|
},
|
|
{
|
|
desc: "get a list with id of existing twin without states ",
|
|
id: tw2.ID,
|
|
token: token,
|
|
offset: 0,
|
|
limit: 10,
|
|
size: 0,
|
|
err: nil,
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
page, err := svc.ListStates(context.TODO(), tc.token, tc.offset, tc.limit, tc.id)
|
|
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
|
|
assert.Equal(t, tc.size, len(page.States), fmt.Sprintf("%s: expected %d total got %d total\n", tc.desc, tc.size, len(page.States)))
|
|
}
|
|
}
|