Skip to content

Commit 517f714

Browse files
committed
Cherry-pick #3045
Signed-off-by: ItalyPaleAle <[email protected]>
1 parent 659e239 commit 517f714

File tree

14 files changed

+296
-21
lines changed

14 files changed

+296
-21
lines changed

.github/scripts/test-info.mjs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,16 @@ const components = {
266266
'crypto.jwks': {
267267
conformance: true,
268268
},
269+
'lock.redis.v6': {
270+
conformance: true,
271+
conformanceSetup: 'docker-compose.sh redisjson redis',
272+
sourcePkg: ['lock/redis', 'internal/component/redis'],
273+
},
274+
'lock.redis.v7': {
275+
conformance: true,
276+
conformanceSetup: 'docker-compose.sh redis7 redis',
277+
sourcePkg: ['lock/redis', 'internal/component/redis'],
278+
},
269279
'middleware.http.bearer': {
270280
certification: true,
271281
},
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
apiVersion: dapr.io/v1alpha1
2+
kind: Component
3+
metadata:
4+
name: lockstore
5+
spec:
6+
type: lock.redis
7+
version: v1
8+
metadata:
9+
- name: redisHost
10+
value: localhost:6379
11+
- name: redisPassword
12+
value: ""
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
apiVersion: dapr.io/v1alpha1
2+
kind: Component
3+
metadata:
4+
name: lockstore
5+
spec:
6+
type: lock.redis
7+
version: v1
8+
metadata:
9+
- name: redisHost
10+
value: localhost:6380
11+
- name: redisPassword
12+
value: ""

tests/config/lock/tests.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Supported additional operations: (none)
2+
componentType: lock
3+
components:
4+
- component: redis.v6
5+
operations: []
6+
- component: redis.v7
7+
operations: []

tests/conformance/bindings_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@ package conformance
1919
import (
2020
"testing"
2121

22-
"github.com/stretchr/testify/assert"
22+
"github.com/stretchr/testify/require"
2323
)
2424

2525
func TestBindingsConformance(t *testing.T) {
2626
tc, err := NewTestConfiguration("../config/bindings/tests.yml")
27-
assert.NoError(t, err)
28-
assert.NotNil(t, tc)
27+
require.NoError(t, err)
28+
require.NotNil(t, tc)
2929
tc.Run(t)
3030
}

tests/conformance/common.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import (
3636
"github.com/dapr/components-contrib/bindings"
3737
"github.com/dapr/components-contrib/configuration"
3838
contribCrypto "github.com/dapr/components-contrib/crypto"
39+
"github.com/dapr/components-contrib/lock"
3940
"github.com/dapr/components-contrib/pubsub"
4041
"github.com/dapr/components-contrib/secretstores"
4142
"github.com/dapr/components-contrib/state"
@@ -63,6 +64,7 @@ import (
6364
cr_azurekeyvault "github.com/dapr/components-contrib/crypto/azure/keyvault"
6465
cr_jwks "github.com/dapr/components-contrib/crypto/jwks"
6566
cr_localstorage "github.com/dapr/components-contrib/crypto/localstorage"
67+
l_redis "github.com/dapr/components-contrib/lock/redis"
6668
p_snssqs "github.com/dapr/components-contrib/pubsub/aws/snssqs"
6769
p_eventhubs "github.com/dapr/components-contrib/pubsub/azure/eventhubs"
6870
p_servicebusqueues "github.com/dapr/components-contrib/pubsub/azure/servicebus/queues"
@@ -105,6 +107,7 @@ import (
105107
conf_bindings "github.com/dapr/components-contrib/tests/conformance/bindings"
106108
conf_configuration "github.com/dapr/components-contrib/tests/conformance/configuration"
107109
conf_crypto "github.com/dapr/components-contrib/tests/conformance/crypto"
110+
conf_lock "github.com/dapr/components-contrib/tests/conformance/lock"
108111
conf_pubsub "github.com/dapr/components-contrib/tests/conformance/pubsub"
109112
conf_secret "github.com/dapr/components-contrib/tests/conformance/secretstores"
110113
conf_state "github.com/dapr/components-contrib/tests/conformance/state"
@@ -402,6 +405,15 @@ func (tc *TestConfiguration) Run(t *testing.T) {
402405
wf := loadWorkflow(comp)
403406
wfConfig := conf_workflows.NewTestConfig(comp.Component, comp.Operations, comp.Config)
404407
conf_workflows.ConformanceTests(t, props, wf, wfConfig)
408+
case "lock":
409+
filepath := fmt.Sprintf("../config/lock/%s", componentConfigPath)
410+
props, err := tc.loadComponentsAndProperties(t, filepath)
411+
require.NoErrorf(t, err, "error running conformance test for component %s", comp.Component)
412+
component := loadLockStore(comp)
413+
require.NotNil(t, component, "error running conformance test for component %s", comp.Component)
414+
lockConfig, err := conf_lock.NewTestConfig(comp.Component, comp.Operations, comp.Config)
415+
require.NoErrorf(t, err, "error running conformance test for component %s", comp.Component)
416+
conf_lock.ConformanceTests(t, props, component, lockConfig)
405417
case "crypto":
406418
filepath := fmt.Sprintf("../config/crypto/%s", componentConfigPath)
407419
props, err := tc.loadComponentsAndProperties(t, filepath)
@@ -525,6 +537,18 @@ func loadCryptoProvider(tc TestComponent) contribCrypto.SubtleCrypto {
525537
return component
526538
}
527539

540+
func loadLockStore(tc TestComponent) lock.Store {
541+
var component lock.Store
542+
switch tc.Component {
543+
case redisv6:
544+
component = l_redis.NewStandaloneRedisLock(testLogger)
545+
case redisv7:
546+
component = l_redis.NewStandaloneRedisLock(testLogger)
547+
}
548+
549+
return component
550+
}
551+
528552
func loadStateStore(tc TestComponent) state.Store {
529553
var store state.Store
530554
switch tc.Component {

tests/conformance/configuration_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@ package conformance
1919
import (
2020
"testing"
2121

22-
"github.com/stretchr/testify/assert"
22+
"github.com/stretchr/testify/require"
2323
)
2424

2525
func TestConfigurationConformance(t *testing.T) {
2626
tc, err := NewTestConfiguration("../config/configuration/tests.yml")
27-
assert.NoError(t, err)
28-
assert.NotNil(t, tc)
27+
require.NoError(t, err)
28+
require.NotNil(t, tc)
2929
tc.Run(t)
3030
}

tests/conformance/crypto_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@ package conformance
1919
import (
2020
"testing"
2121

22-
"github.com/stretchr/testify/assert"
22+
"github.com/stretchr/testify/require"
2323
)
2424

2525
func TestCryptoConformance(t *testing.T) {
2626
tc, err := NewTestConfiguration("../config/crypto/tests.yml")
27-
assert.NoError(t, err)
28-
assert.NotNil(t, tc)
27+
require.NoError(t, err)
28+
require.NotNil(t, tc)
2929
tc.Run(t)
3030
}

tests/conformance/lock/lock.go

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
/*
2+
Copyright 2021 The Dapr Authors
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
http://www.apache.org/licenses/LICENSE-2.0
7+
Unless required by applicable law or agreed to in writing, software
8+
distributed under the License is distributed on an "AS IS" BASIS,
9+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
14+
package state
15+
16+
import (
17+
"context"
18+
"strings"
19+
"testing"
20+
"time"
21+
22+
"github.com/google/uuid"
23+
"github.com/stretchr/testify/assert"
24+
"github.com/stretchr/testify/require"
25+
26+
"github.com/dapr/components-contrib/lock"
27+
"github.com/dapr/components-contrib/metadata"
28+
"github.com/dapr/components-contrib/tests/conformance/utils"
29+
"github.com/dapr/kit/config"
30+
)
31+
32+
type TestConfig struct {
33+
utils.CommonConfig
34+
}
35+
36+
func NewTestConfig(component string, operations []string, configMap map[string]interface{}) (TestConfig, error) {
37+
testConfig := TestConfig{
38+
CommonConfig: utils.CommonConfig{
39+
ComponentType: "lock",
40+
ComponentName: component,
41+
Operations: utils.NewStringSet(operations...),
42+
},
43+
}
44+
45+
err := config.Decode(configMap, &testConfig)
46+
if err != nil {
47+
return testConfig, err
48+
}
49+
50+
return testConfig, nil
51+
}
52+
53+
// ConformanceTests runs conf tests for lock stores.
54+
func ConformanceTests(t *testing.T, props map[string]string, lockstore lock.Store, config TestConfig) {
55+
// Test vars
56+
key := strings.ReplaceAll(uuid.New().String(), "-", "")
57+
t.Logf("Base key for test: %s", key)
58+
59+
t.Run("init", func(t *testing.T) {
60+
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
61+
defer cancel()
62+
err := lockstore.InitLockStore(ctx, lock.Metadata{Base: metadata.Base{
63+
Properties: props,
64+
}})
65+
require.NoError(t, err)
66+
})
67+
68+
// Don't run more tests if init failed
69+
if t.Failed() {
70+
t.Fatal("Init failed, stopping further tests")
71+
}
72+
73+
const lockOwner = "conftest"
74+
lockKey1 := key + "-1"
75+
lockKey2 := key + "-2"
76+
77+
var expirationCh *time.Timer
78+
79+
t.Run("TryLock", func(t *testing.T) {
80+
// Acquire a lock
81+
t.Run("acquire lock1", func(t *testing.T) {
82+
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
83+
defer cancel()
84+
res, err := lockstore.TryLock(ctx, &lock.TryLockRequest{
85+
ResourceID: lockKey1,
86+
LockOwner: lockOwner,
87+
ExpiryInSeconds: 15,
88+
})
89+
require.NoError(t, err)
90+
require.NotNil(t, res)
91+
assert.True(t, res.Success)
92+
})
93+
94+
// Acquire a second lock (with a shorter expiration)
95+
t.Run("acquire lock2", func(t *testing.T) {
96+
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
97+
defer cancel()
98+
res, err := lockstore.TryLock(ctx, &lock.TryLockRequest{
99+
ResourceID: lockKey2,
100+
LockOwner: lockOwner,
101+
ExpiryInSeconds: 3,
102+
})
103+
require.NoError(t, err)
104+
require.NotNil(t, res)
105+
assert.True(t, res.Success)
106+
107+
// Set expirationCh to when lock2 expires
108+
expirationCh = time.NewTimer(3 * time.Second)
109+
})
110+
111+
// Acquiring the same lock again should fail
112+
t.Run("fails to acquire existing lock", func(t *testing.T) {
113+
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
114+
defer cancel()
115+
res, err := lockstore.TryLock(ctx, &lock.TryLockRequest{
116+
ResourceID: lockKey1,
117+
LockOwner: lockOwner,
118+
ExpiryInSeconds: 15,
119+
})
120+
require.NoError(t, err)
121+
require.NotNil(t, res)
122+
assert.False(t, res.Success)
123+
})
124+
})
125+
126+
t.Run("Unlock", func(t *testing.T) {
127+
t.Run("fails to unlock with nonexistent resource ID", func(t *testing.T) {
128+
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
129+
defer cancel()
130+
res, err := lockstore.Unlock(ctx, &lock.UnlockRequest{
131+
ResourceID: "nonexistent",
132+
LockOwner: lockOwner,
133+
})
134+
require.NoError(t, err)
135+
require.NotNil(t, res)
136+
assert.Equal(t, lock.LockDoesNotExist, res.Status)
137+
})
138+
139+
t.Run("fails to unlock with wrong owner", func(t *testing.T) {
140+
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
141+
defer cancel()
142+
res, err := lockstore.Unlock(ctx, &lock.UnlockRequest{
143+
ResourceID: lockKey1,
144+
LockOwner: "nonowner",
145+
})
146+
require.NoError(t, err)
147+
require.NotNil(t, res)
148+
assert.Equal(t, lock.LockBelongsToOthers, res.Status)
149+
})
150+
151+
t.Run("unlocks successfully", func(t *testing.T) {
152+
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
153+
defer cancel()
154+
res, err := lockstore.Unlock(ctx, &lock.UnlockRequest{
155+
ResourceID: lockKey1,
156+
LockOwner: lockOwner,
157+
})
158+
require.NoError(t, err)
159+
require.NotNil(t, res)
160+
assert.Equal(t, lock.Success, res.Status)
161+
})
162+
})
163+
164+
t.Run("lock expires", func(t *testing.T) {
165+
// Wait until the lock is supposed to expire
166+
<-expirationCh.C
167+
168+
// Assert that the lock doesn't exist anymore - we should be able to re-acquire it
169+
assert.Eventually(t, func() bool {
170+
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
171+
defer cancel()
172+
res, err := lockstore.TryLock(ctx, &lock.TryLockRequest{
173+
ResourceID: lockKey2,
174+
LockOwner: lockOwner,
175+
ExpiryInSeconds: 3,
176+
})
177+
return err == nil && res != nil && res.Success
178+
}, 5*time.Second, 100*time.Millisecond, "Lock 2 was not released in time after its scheduled expiration")
179+
})
180+
}

tests/conformance/lock_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//go:build conftests
2+
// +build conftests
3+
4+
/*
5+
Copyright 2023 The Dapr Authors
6+
Licensed under the Apache License, Version 2.0 (the "License");
7+
you may not use this file except in compliance with the License.
8+
You may obtain a copy of the License at
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package conformance
18+
19+
import (
20+
"testing"
21+
22+
"github.com/stretchr/testify/require"
23+
)
24+
25+
func TestLockConformance(t *testing.T) {
26+
tc, err := NewTestConfiguration("../config/lock/tests.yml")
27+
require.NoError(t, err)
28+
require.NotNil(t, tc)
29+
tc.Run(t)
30+
}

0 commit comments

Comments
 (0)