Skip to content

Commit 3723f8b

Browse files
committed
Merge pull request #38 from guersam/tmp/improve-test
Enhance test cases and documentation
2 parents 4d2ecb4 + 6e79570 commit 3723f8b

File tree

8 files changed

+349
-215
lines changed

8 files changed

+349
-215
lines changed

README.md

Lines changed: 122 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,11 @@ A non blocking Redis client based on Akka I/O
44

55
### Key features of the library
66

7-
- Non blocking
8-
- Full set of Redis commands
9-
- Scripting support
10-
- Transparent typeclass based serialization
11-
- Out of the box integration support with Json serialization libraries
12-
- Composable with Futures
7+
- [Non blocking, compositional with Futures](#non-blocking-compositional-with-futures)
8+
- [Full set of Redis commands](#full-set-of-redis-commands)
9+
- [Scripting support](#scripting-support)
10+
- [Transparent typeclass based serialization](#typeclass-based-transparent-serialization)
11+
- [Out of the box integration support with Json serialization libraries](#integration-support-with-json-serializing-libraries)
1312

1413
### Project Dependencies
1514

@@ -37,7 +36,14 @@ implicit val timeout = AkkaTimeout(5 seconds)
3736
val client = RedisClient("localhost", 6379)
3837
```
3938

40-
#### Non Blocking Compositional Gets and Sets
39+
**Note**
40+
41+
The below examples are taken from the test cases.
42+
43+
Every API call returns Scala [`Future`](http://www.scala-lang.org/api/current/index.html#scala.concurrent.Future) and `_.futureValue` is just a helper provided by [ScalaTest](http://scalatest.org) for ease of test. You won't need this in most cases because it blocks current thread.
44+
45+
46+
#### Non Blocking, Compositional with Futures
4147

4248
```scala
4349
describe("set") {
@@ -74,7 +80,42 @@ describe("get") {
7480
}
7581
```
7682

77-
#### List Operations
83+
```scala
84+
describe("non blocking apis using futures") {
85+
// ...
86+
87+
it("should compose with sequential combinator") {
88+
val key = "client_key_seq"
89+
90+
val res = for {
91+
p <- client.lpush(key, 0 to 100)
92+
if p > 0
93+
r <- client.lrange[Long](key, 0, -1)
94+
} yield (p, r)
95+
96+
val (count, list) = res.futureValue
97+
count should equal (101)
98+
list.reverse should equal (0 to 100)
99+
}
100+
}
101+
102+
describe("error handling using promise failure") {
103+
it("should give error trying to lpush on a key that has a non list value") {
104+
val key = "client_err"
105+
client.set(key, "value200").futureValue should be (true)
106+
107+
val thrown = evaluating {
108+
client.lpush(key, 1200).futureValue
109+
} should produce [TestFailedException]
110+
111+
thrown.getCause.getMessage should equal ("ERR Operation against a key holding the wrong kind of value")
112+
}
113+
}
114+
```
115+
116+
#### Full set of Redis commands
117+
118+
##### List Operations
78119

79120
```scala
80121
describe("lpush") {
@@ -97,28 +138,39 @@ describe("lpush") {
97138
}
98139
```
99140

100-
#### Other Data Structure support as per Redis
141+
##### Other Data Structure support as per Redis
101142

102143
```scala
103144
// Hash
104145
describe("hmget") {
105-
it("should set and get maps") {
106-
val key = "hmget1"
107-
client.hmset(key, Map("field1" -> "val1", "field2" -> "val2"))
108-
client.hmget(key, "field1").futureValue should equal (Map("field1" -> "val1"))
109-
client.hmget(key, "field1", "field2").futureValue should equal (Map("field1" -> "val1", "field2" -> "val2"))
110-
client.hmget(key, "field1", "field2", "field3").futureValue should equal (Map("field1" -> "val1", "field2" -> "val2"))
111-
}
146+
it("should set and get maps") {
147+
val key = "hmget1"
148+
client.hmset(key, Map("field1" -> "val1", "field2" -> "val2"))
149+
150+
client
151+
.hmget(key, "field1")
152+
.futureValue should equal (Map("field1" -> "val1"))
153+
154+
client
155+
.hmget(key, "field1", "field2")
156+
.futureValue should equal (Map("field1" -> "val1", "field2" -> "val2"))
157+
158+
client
159+
.hmget(key, "field1", "field2", "field3")
160+
.futureValue should equal (Map("field1" -> "val1", "field2" -> "val2"))
161+
}
112162
}
113163

114164
// Set
115165
describe("spop") {
116166
it("should pop a random element") {
117167
val key = "spop1"
118-
client.sadd(key, "foo").futureValue should equal (1)
119-
client.sadd(key, "bar").futureValue should equal (1)
120-
client.sadd(key, "baz").futureValue should equal (1)
121-
client.spop(key).futureValue should (equal (Some("foo")) or equal (Some("bar")) or equal (Some("baz")))
168+
client.sadd(key, "foo")
169+
client.sadd(key, "bar")
170+
171+
client
172+
.spop(key)
173+
.futureValue should (equal (Some("foo")) or equal (Some("bar")))
122174
}
123175

124176
it("should return nil if the key does not exist") {
@@ -132,89 +184,31 @@ describe("z(rev)rangeByScoreWithScore") {
132184
it ("should return the elements between min and max") {
133185
add
134186

135-
client.zrangeByScoreWithScores("hackers", 1940, true, 1969, true, None).futureValue should equal (
136-
List(("alan kay", 1940.0), ("richard stallman", 1953.0), ("yukihiro matsumoto", 1965.0), ("linus torvalds", 1969.0)))
187+
client
188+
.zrangeByScoreWithScores("hackers", 1940, true, 1969, true, None)
189+
.futureValue should equal (List(
190+
("alan kay", 1940.0), ("richard stallman", 1953.0),
191+
("yukihiro matsumoto", 1965.0), ("linus torvalds", 1969.0)
192+
))
137193

138-
client.zrevrangeByScoreWithScores("hackers", 1940, true, 1969, true, None).futureValue should equal (
139-
List(("linus torvalds", 1969.0), ("yukihiro matsumoto", 1965.0), ("richard stallman", 1953.0),("alan kay", 1940.0)))
194+
client
195+
.zrevrangeByScoreWithScores("hackers", 1940, true, 1969, true, None)
196+
.futureValue should equal (List(
197+
("linus torvalds", 1969.0), ("yukihiro matsumoto", 1965.0),
198+
("richard stallman", 1953.0),("alan kay", 1940.0)
199+
))
140200

141-
client.zrangeByScoreWithScores("hackers", 1940, true, 1969, true, Some(3, 1)).futureValue should equal (
142-
List(("linus torvalds", 1969.0)))
201+
client
202+
.zrangeByScoreWithScores("hackers", 1940, true, 1969, true, Some(3, 1))
203+
.futureValue should equal (List(("linus torvalds", 1969.0)))
143204

144-
client.zrevrangeByScoreWithScores("hackers", 1940, true, 1969, true, Some(3, 1)).futureValue should equal (
145-
List(("alan kay", 1940.0)))
205+
client
206+
.zrevrangeByScoreWithScores("hackers", 1940, true, 1969, true, Some(3, 1))
207+
.futureValue should equal (List(("alan kay", 1940.0)))
146208
}
147209
}
148210
```
149211

150-
#### With Operations that compose
151-
152-
```scala
153-
describe("non blocking apis using futures") {
154-
it("get and set should be non blocking") {
155-
@volatile var callbackExecuted = false
156-
157-
val ks = (1 to 10).map(i => s"client_key_$i")
158-
val kvs = ks.zip(1 to 10)
159-
160-
val sets: Seq[Future[Boolean]] = kvs map {
161-
case (k, v) => client.set(k, v)
162-
}
163-
164-
val setResult = Future.sequence(sets) map { r: Seq[Boolean] =>
165-
callbackExecuted = true
166-
r
167-
}
168-
169-
callbackExecuted should be (false)
170-
setResult.futureValue should contain only (true)
171-
callbackExecuted should be (true)
172-
173-
callbackExecuted = false
174-
val gets: Seq[Future[Option[Long]]] = ks.map { k => client.get[Long](k) }
175-
val getResult = Future.sequence(gets).map { rs =>
176-
callbackExecuted = true
177-
rs.flatten.sum
178-
}
179-
180-
callbackExecuted should be (false)
181-
getResult.futureValue should equal (55)
182-
callbackExecuted should be (true)
183-
}
184-
185-
it("should compose with sequential combinator") {
186-
val key = "client_key_seq"
187-
val values = (1 to 100).toList
188-
val pushResult = client.lpush(key, 0, values:_*)
189-
val getResult = client.lrange[Long](key, 0, -1)
190-
191-
val res = for {
192-
p <- pushResult.mapTo[Long]
193-
if p > 0
194-
r <- getResult.mapTo[List[Long]]
195-
} yield (p, r)
196-
197-
val (count, list) = res.futureValue
198-
count should equal (101)
199-
list.reverse should equal (0 to 100)
200-
}
201-
}
202-
```
203-
204-
```scala
205-
describe("error handling using promise failure") {
206-
it("should give error trying to lpush on a key that has a non list value") {
207-
val key = "client_err"
208-
val v = client.set(key, "value200")
209-
v.futureValue should be (true)
210-
211-
val x = client.lpush(key, 1200)
212-
val thrown = evaluating { x.futureValue } should produce [TestFailedException]
213-
thrown.getCause.getMessage should equal ("ERR Operation against a key holding the wrong kind of value")
214-
}
215-
}
216-
```
217-
218212
#### Scripting support
219213

220214
```scala
@@ -312,25 +306,39 @@ describe("eval") {
312306
```scala
313307
import DefaultFormats._
314308

315-
client.hmset("hash", Map("field1" -> "1", "field2" -> 2)).futureValue should be (true)
316-
client.hmget[String]("hash", "field1", "field2").futureValue should be(Map("field1" -> "1", "field2" -> "2"))
317-
client.hmget[Int]("hash", "field1", "field2").futureValue should be(Map("field1" -> 1, "field2" -> 2))
318-
client.hmget[Int]("hash", "field1", "field2", "field3").futureValue should be(Map("field1" -> 1, "field2" -> 2))
309+
client
310+
.hmset("hash", Map("field1" -> "1", "field2" -> 2))
311+
.futureValue should be (true)
312+
313+
client
314+
.hmget[String]("hash", "field1", "field2")
315+
.futureValue should equal (Map("field1" -> "1", "field2" -> "2"))
316+
317+
client
318+
.hmget[Int]("hash", "field1", "field2")
319+
.futureValue should equal (Map("field1" -> 1, "field2" -> 2))
320+
321+
client
322+
.hmget[Int]("hash", "field1", "field2", "field3")
323+
.futureValue should equal (Map("field1" -> 1, "field2" -> 2))
319324
```
320325

321326
###### Should use a provided implicit Format typeclass
322327

323328
```scala
324329
import DefaultFormats._
325330

326-
client.hmset("hash", Map("field1" -> "1", "field2" -> 2)).futureValue should be (true)
327-
client.hmget("hash", "field1", "field2").futureValue should be(Map("field1" -> "1", "field2" -> "2"))
331+
client.hmset("hash", Map("field1" -> "1", "field2" -> 2))
328332

329333
implicit val intFormat = Format[Int](java.lang.Integer.parseInt, _.toString)
330334

331-
client.hmget[Int]("hash", "field1", "field2").futureValue should be(Map("field1" -> 1, "field2" -> 2))
332-
client.hmget[String]("hash", "field1", "field2").futureValue should be(Map("field1" -> "1", "field2" -> "2"))
333-
client.hmget[Int]("hash", "field1", "field2", "field3").futureValue should be(Map("field1" -> 1, "field2" -> 2))
335+
client
336+
.hmget[Int]("hash", "field1", "field2")
337+
.futureValue should equal (Map("field1" -> 1, "field2" -> 2))
338+
339+
client
340+
.hmget[String]("hash", "field1", "field2")
341+
.futureValue should be(Map("field1" -> "1", "field2" -> "2"))
334342
```
335343

336344
###### Easy to have custom Format for case classes
@@ -340,7 +348,6 @@ case class Person(id: Int, name: String)
340348

341349
val debasish = Person(1, "Debasish Gosh")
342350
val jisoo = Person(2, "Jisoo Park")
343-
val people = List(debasish, jisoo)
344351

345352
implicit val customPersonFormat =
346353
new Format[Person] {
@@ -358,32 +365,30 @@ implicit val customPersonFormat =
358365
}
359366
}
360367

361-
val write = implicitly[Write[Person]].write _
362-
val read = implicitly[Read[Person]].read _
368+
client.set("debasish", debasish)
363369

364-
read(write(debasish)) should equal (debasish)
370+
client.get[Person]("debasish").futureValue should equal (Some(debasish))
365371
```
366372

367373
###### Integration support with Json Serializing libraries
368374

369375
```scala
370376
import spray.json.DefaultJsonProtocol._
371-
import SprayJsonSupport._
377+
import com.redis.serialization.SprayJsonSupport._
372378

373379
implicit val personFormat = jsonFormat2(Person)
374380

375-
val write = implicitly[Write[Person]].write _
376-
val read = implicitly[Read[Person]].read _
381+
client.set("debasish", debasish)
382+
client.set("people", List(debasish, jisoo))
377383

378-
val writeL = implicitly[Write[List[Person]]].write _
379-
val readL = implicitly[Read[List[Person]]].read _
380-
381-
read(write(debasish)) should equal (debasish)
382-
readL(writeL(people)) should equal (people)
384+
client.get[Person]("debasish").futureValue should equal (Some(debasish))
385+
client.get[List[Person]]("people").futureValue should equal (Some(List(debasish, jisoo)))
383386
```
384387

385388
For more examples on serialization, have a look at the test cases.
386389

390+
_Third-party libraries are not installed by default. You need to provide them by yourself._
391+
387392
### License
388393

389394
This software is licensed under the Apache 2 license, quoted below.

src/test/scala/com/redis/ClientSpec.scala

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,11 @@ class ClientSpec extends RedisSpecBase {
4848

4949
it("should compose with sequential combinator") {
5050
val key = "client_key_seq"
51-
val values = (1 to 100).toList
52-
val pushResult = client.lpush(key, 0, values:_*)
53-
val getResult = client.lrange[Long](key, 0, -1)
5451

5552
val res = for {
56-
p <- pushResult.mapTo[Long]
53+
p <- client.lpush(key, 0 to 100)
5754
if p > 0
58-
r <- getResult.mapTo[List[Long]]
55+
r <- client.lrange[Long](key, 0, -1)
5956
} yield (p, r)
6057

6158
val (count, list) = res.futureValue
@@ -67,11 +64,12 @@ class ClientSpec extends RedisSpecBase {
6764
describe("error handling using promise failure") {
6865
it("should give error trying to lpush on a key that has a non list value") {
6966
val key = "client_err"
70-
val v = client.set(key, "value200")
71-
v.futureValue should be (true)
67+
client.set(key, "value200").futureValue should be (true)
68+
69+
val thrown = evaluating {
70+
client.lpush(key, 1200).futureValue
71+
} should produce [TestFailedException]
7272

73-
val x = client.lpush(key, 1200)
74-
val thrown = evaluating { x.futureValue } should produce [TestFailedException]
7573
thrown.getCause.getMessage should equal ("ERR Operation against a key holding the wrong kind of value")
7674
}
7775
}

0 commit comments

Comments
 (0)