Skip to content

Commit 48be716

Browse files
author
Mikhail Podtserkovskiy
committed
local storage + tests
1 parent a6de600 commit 48be716

File tree

7 files changed

+316
-6
lines changed

7 files changed

+316
-6
lines changed

invoke.go

+3
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"github.com/qa-dev/jsonwire-grid/pool/strategy/kubernetes"
1212
"github.com/qa-dev/jsonwire-grid/pool/strategy/persistent"
1313
"github.com/qa-dev/jsonwire-grid/selenium"
14+
"github.com/qa-dev/jsonwire-grid/storage/local"
1415
"github.com/qa-dev/jsonwire-grid/storage/mysql"
1516
"github.com/qa-dev/jsonwire-grid/wda"
1617
)
@@ -27,6 +28,8 @@ func invokeStorageFactory(config config.Config) (factory StorageFactoryInterface
2728
switch config.DB.Implementation {
2829
case "mysql":
2930
factory = new(mysql.Factory)
31+
case "local":
32+
factory = new(local.Factory)
3033
default:
3134
err = errors.New("Invalid config, unknown param [db.implementation=" + config.DB.Implementation + "]")
3235
}

pool/strategy/persistent/nodeHelper_test.go

+4-5
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
package persistent
22

33
import (
4-
"testing"
5-
"github.com/stretchr/testify/assert"
6-
"github.com/qa-dev/jsonwire-grid/jsonwire"
74
"encoding/json"
8-
"github.com/stretchr/testify/mock"
95
"errors"
6+
"github.com/qa-dev/jsonwire-grid/jsonwire"
7+
"github.com/stretchr/testify/assert"
8+
"github.com/stretchr/testify/mock"
9+
"testing"
1010
)
1111

1212
func TestNodeHelperFactory_create(t *testing.T) {
@@ -93,4 +93,3 @@ func TestNodeHelper_removeAllSessions_Negative_CloseSession_MessageStatusNotOk(t
9393
_, err := nodeHelper.removeAllSessions()
9494
assert.NotNil(t, err)
9595
}
96-

storage/local/factory.go

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package local
2+
3+
import (
4+
"github.com/qa-dev/jsonwire-grid/config"
5+
"github.com/qa-dev/jsonwire-grid/pool"
6+
)
7+
8+
type Factory struct {
9+
}
10+
11+
func (f *Factory) Create(cfg config.Config) (pool.StorageInterface, error) {
12+
return &Storage{db: make(map[string]*pool.Node)}, nil
13+
}

storage/local/factory_test.go

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package local
2+
3+
import (
4+
"github.com/qa-dev/jsonwire-grid/config"
5+
"github.com/stretchr/testify/assert"
6+
"testing"
7+
)
8+
9+
func TestFactory_Create_Positive(t *testing.T) {
10+
f := Factory{}
11+
storage, err := f.Create(config.Config{})
12+
assert.NoError(t, err)
13+
assert.NotNil(t, storage)
14+
}

storage/local/local_test.go

+151
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
package local
2+
3+
import (
4+
"github.com/qa-dev/jsonwire-grid/pool"
5+
"github.com/qa-dev/jsonwire-grid/storage"
6+
"github.com/stretchr/testify/assert"
7+
"testing"
8+
)
9+
10+
func TestStorage_Add_Positive(t *testing.T) {
11+
s := Storage{db: map[string]*pool.Node{}}
12+
err := s.Add(pool.Node{}, 0)
13+
assert.NoError(t, err)
14+
assert.Len(t, s.db, 1)
15+
}
16+
17+
func TestStorage_Add_Positive_Repeat(t *testing.T) {
18+
s := Storage{db: map[string]*pool.Node{"1": {Address: "1"}}}
19+
err := s.Add(pool.Node{Address: "1"}, 0)
20+
assert.NoError(t, err)
21+
assert.Len(t, s.db, 1)
22+
}
23+
24+
func TestStorage_Add_Negative_LimitReached(t *testing.T) {
25+
s := Storage{db: map[string]*pool.Node{"1": {Address: "1"}}}
26+
limit := 1
27+
err := s.Add(pool.Node{Address: "2"}, limit)
28+
assert.Error(t, err, "limit reached")
29+
assert.Len(t, s.db, limit)
30+
}
31+
32+
func TestStorage_ReserveAvailable_Positive(t *testing.T) {
33+
expectedNode := pool.Node{Address: "1", Status: pool.NodeStatusAvailable}
34+
s := Storage{db: map[string]*pool.Node{expectedNode.Address: &expectedNode}}
35+
node, err := s.ReserveAvailable([]pool.Node{expectedNode})
36+
assert.NoError(t, err)
37+
assert.Equal(t, expectedNode, node)
38+
assert.Equal(t, pool.NodeStatusReserved, s.db[node.Address].Status)
39+
}
40+
41+
func TestStorage_ReserveAvailable_Negative_NotFoundAvailableNodes(t *testing.T) {
42+
expectedNode := pool.Node{Address: "1", Status: pool.NodeStatusBusy}
43+
s := Storage{db: map[string]*pool.Node{expectedNode.Address: &expectedNode}}
44+
_, err := s.ReserveAvailable([]pool.Node{expectedNode})
45+
assert.Error(t, err, storage.ErrNotFound)
46+
}
47+
48+
func TestStorage_ReserveAvailable_Negative_InvalidNodeList(t *testing.T) {
49+
s := Storage{db: map[string]*pool.Node{}}
50+
_, err := s.ReserveAvailable([]pool.Node{{Address: "awd"}})
51+
assert.Error(t, err, storage.ErrNotFound)
52+
}
53+
54+
func TestStorage_SetBusy_Positive(t *testing.T) {
55+
expectedNode := pool.Node{Address: "1"}
56+
s := Storage{db: map[string]*pool.Node{expectedNode.Address: &expectedNode}}
57+
expectedSessionID := "expectedSessionID"
58+
err := s.SetBusy(expectedNode, expectedSessionID)
59+
assert.NoError(t, err)
60+
assert.Equal(t, pool.NodeStatusBusy, s.db[expectedNode.Address].Status)
61+
assert.Equal(t, expectedSessionID, s.db[expectedNode.Address].SessionID)
62+
}
63+
64+
func TestStorage_SetBusy_Negative(t *testing.T) {
65+
expectedNode := pool.Node{Address: "1"}
66+
s := Storage{db: map[string]*pool.Node{}}
67+
expectedSessionID := "expectedSessionID"
68+
err := s.SetBusy(expectedNode, expectedSessionID)
69+
assert.Error(t, err, storage.ErrNotFound)
70+
}
71+
72+
func TestStorage_SetAvailable_Positive(t *testing.T) {
73+
expectedNode := pool.Node{Address: "1"}
74+
s := Storage{db: map[string]*pool.Node{expectedNode.Address: &expectedNode}}
75+
err := s.SetAvailable(expectedNode)
76+
assert.NoError(t, err)
77+
assert.Equal(t, pool.NodeStatusAvailable, s.db[expectedNode.Address].Status)
78+
}
79+
80+
func TestStorage_SetAvailable_Negative(t *testing.T) {
81+
expectedNode := pool.Node{Address: "1"}
82+
s := Storage{db: map[string]*pool.Node{}}
83+
err := s.SetAvailable(expectedNode)
84+
assert.Error(t, err, storage.ErrNotFound)
85+
}
86+
87+
func TestStorage_GetCountWithStatus_Positive_All(t *testing.T) {
88+
s := Storage{db: map[string]*pool.Node{"1": {Address: "1"}, "2": {Address: "2"}}}
89+
count, err := s.GetCountWithStatus(nil)
90+
assert.NoError(t, err)
91+
assert.Equal(t, count, len(s.db))
92+
}
93+
94+
func TestStorage_GetCountWithStatus_Positive_One(t *testing.T) {
95+
expectedStatus := pool.NodeStatusBusy
96+
s := Storage{db: map[string]*pool.Node{"1": {Address: "1", Status: expectedStatus}, "2": {Address: "2"}}}
97+
count, err := s.GetCountWithStatus(&expectedStatus)
98+
assert.NoError(t, err)
99+
assert.Equal(t, count, 1)
100+
}
101+
102+
func TestStorage_GetBySession_Positive(t *testing.T) {
103+
expectedNode := pool.Node{Address: "1"}
104+
s := Storage{db: map[string]*pool.Node{expectedNode.Address: &expectedNode}}
105+
node, err := s.GetBySession(expectedNode.SessionID)
106+
assert.NoError(t, err)
107+
assert.Equal(t, expectedNode, node)
108+
}
109+
110+
func TestStorage_GetBySession_Negative(t *testing.T) {
111+
expectedNode := pool.Node{Address: "1"}
112+
s := Storage{db: map[string]*pool.Node{}}
113+
_, err := s.GetBySession(expectedNode.SessionID)
114+
assert.Error(t, err, storage.ErrNotFound)
115+
}
116+
117+
func TestStorage_GetByAddress_Positive(t *testing.T) {
118+
expectedNode := pool.Node{Address: "1"}
119+
s := Storage{db: map[string]*pool.Node{expectedNode.Address: &expectedNode}}
120+
node, err := s.GetByAddress(expectedNode.Address)
121+
assert.NoError(t, err)
122+
assert.Equal(t, expectedNode, node)
123+
}
124+
125+
func TestStorage_GetByAddress_Negative(t *testing.T) {
126+
expectedNode := pool.Node{Address: "1"}
127+
s := Storage{db: map[string]*pool.Node{}}
128+
_, err := s.GetByAddress(expectedNode.Address)
129+
assert.Error(t, err, storage.ErrNotFound)
130+
}
131+
132+
func TestStorage_GetAll_Positive(t *testing.T) {
133+
s := Storage{db: map[string]*pool.Node{"1": {Address: "1"}, "2": {Address: "2"}}}
134+
nodeList, err := s.GetAll()
135+
assert.NoError(t, err)
136+
assert.Len(t, nodeList, 2)
137+
}
138+
139+
func TestStorage_Remove_Positive(t *testing.T) {
140+
node := pool.Node{Address: "1"}
141+
s := Storage{db: map[string]*pool.Node{node.Address: &node}}
142+
err := s.Remove(node)
143+
assert.NoError(t, err)
144+
}
145+
146+
func TestStorage_Remove_Negative(t *testing.T) {
147+
s := Storage{db: map[string]*pool.Node{}}
148+
node := pool.Node{Address: "1"}
149+
err := s.Remove(node)
150+
assert.Error(t, err, storage.ErrNotFound)
151+
}

storage/local/storage.go

+130
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
package local
2+
3+
import (
4+
"errors"
5+
"github.com/qa-dev/jsonwire-grid/pool"
6+
"github.com/qa-dev/jsonwire-grid/storage"
7+
"sync"
8+
"time"
9+
)
10+
11+
type Storage struct {
12+
mu sync.RWMutex
13+
db map[string]*pool.Node
14+
}
15+
16+
func (s *Storage) Add(node pool.Node, limit int) error {
17+
s.mu.Lock()
18+
defer s.mu.Unlock()
19+
if limit > 0 {
20+
i := 0
21+
for _, currNode := range s.db {
22+
if currNode.Status == node.Status {
23+
i++
24+
}
25+
}
26+
if i >= limit {
27+
return errors.New("limit reached")
28+
}
29+
}
30+
31+
s.db[node.Address] = &node
32+
return nil
33+
}
34+
35+
func (s *Storage) ReserveAvailable(nodeList []pool.Node) (pool.Node, error) {
36+
s.mu.Lock()
37+
defer s.mu.Unlock()
38+
for _, node := range nodeList {
39+
dbNode, ok := s.db[node.Address]
40+
if ok && dbNode.Status == pool.NodeStatusAvailable {
41+
dbNode.Status = pool.NodeStatusReserved
42+
dbNode.Updated = time.Now().Unix()
43+
return *dbNode, nil
44+
}
45+
}
46+
return pool.Node{}, storage.ErrNotFound
47+
}
48+
49+
func (s *Storage) SetBusy(node pool.Node, sessionID string) error {
50+
s.mu.Lock()
51+
defer s.mu.Unlock()
52+
storedNode, ok := s.db[node.Address]
53+
if !ok {
54+
return storage.ErrNotFound
55+
}
56+
storedNode.Status = pool.NodeStatusBusy
57+
storedNode.SessionID = sessionID
58+
storedNode.Updated = time.Now().Unix()
59+
return nil
60+
}
61+
62+
func (s *Storage) SetAvailable(node pool.Node) error {
63+
s.mu.Lock()
64+
defer s.mu.Unlock()
65+
storedNode, ok := s.db[node.Address]
66+
if !ok {
67+
return storage.ErrNotFound
68+
}
69+
storedNode.Status = pool.NodeStatusAvailable
70+
storedNode.Updated = time.Now().Unix()
71+
return nil
72+
}
73+
74+
func (s *Storage) GetCountWithStatus(status *pool.NodeStatus) (int, error) {
75+
s.mu.RLock()
76+
defer s.mu.RUnlock()
77+
if status == nil {
78+
return len(s.db), nil
79+
}
80+
count := 0
81+
for _, node := range s.db {
82+
if node.Status == *status {
83+
count++
84+
}
85+
}
86+
return count, nil
87+
}
88+
89+
func (s *Storage) GetBySession(sessionID string) (pool.Node, error) {
90+
s.mu.RLock()
91+
defer s.mu.RUnlock()
92+
for _, node := range s.db {
93+
if node.SessionID == sessionID {
94+
return *node, nil
95+
}
96+
}
97+
return pool.Node{}, storage.ErrNotFound
98+
}
99+
100+
func (s *Storage) GetByAddress(address string) (pool.Node, error) {
101+
s.mu.RLock()
102+
defer s.mu.RUnlock()
103+
node, ok := s.db[address]
104+
if !ok {
105+
return pool.Node{}, storage.ErrNotFound
106+
}
107+
return *node, nil
108+
}
109+
110+
func (s *Storage) GetAll() ([]pool.Node, error) {
111+
s.mu.RLock()
112+
defer s.mu.RUnlock()
113+
nodeList := make([]pool.Node, 0, len(s.db))
114+
for _, value := range s.db {
115+
nodeList = append(nodeList, *value)
116+
}
117+
118+
return nodeList, nil
119+
}
120+
121+
func (s *Storage) Remove(node pool.Node) error {
122+
s.mu.Lock()
123+
defer s.mu.Unlock()
124+
_, ok := s.db[node.Address]
125+
if !ok {
126+
return storage.ErrNotFound
127+
}
128+
delete(s.db, node.Address)
129+
return nil
130+
}

storage/storage.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@ import (
55
)
66

77
var (
8-
ErrNotFound = errors.New("storage: not found available nodes")
8+
ErrNotFound = errors.New("storage: node not found ")
99
)

0 commit comments

Comments
 (0)