Skip to content

Commit d8e5764

Browse files
committed
Move to a new file. Restore original test
1 parent 493008b commit d8e5764

File tree

2 files changed

+256
-64
lines changed

2 files changed

+256
-64
lines changed

shopping-cart/shopping-cart-scala/shopping-cart/src/test/scala/com/example/shoppingcart/impl/ShoppingCartEntitySpec.scala

Lines changed: 174 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -2,81 +2,191 @@ package com.example.shoppingcart.impl
22

33
import java.util.UUID
44

5-
import akka.actor.ActorSystem
6-
import akka.actor.BootstrapSetup
7-
import akka.actor.setup.ActorSystemSetup
5+
import akka.actor.testkit.typed.scaladsl.LogCapturing
86
import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit
9-
import akka.actor.typed
10-
import akka.persistence.testkit.scaladsl.EventSourcedBehaviorTestKit
117
import akka.persistence.typed.PersistenceId
12-
import com.example.shoppingcart.impl.ShoppingCart.AddItem
13-
import com.example.shoppingcart.impl.ShoppingCart.Confirmation
14-
import com.lightbend.lagom.scaladsl.playjson.JsonSerializerRegistry
15-
import com.typesafe.config.Config
16-
import com.typesafe.config.ConfigFactory
178
import org.scalatest.wordspec.AnyWordSpecLike
189

19-
20-
/**
21-
* ConfigFactory.load will read the serialization settings from application.conf
22-
*/
2310
class ShoppingCartEntitySpec
24-
extends AbstractShoppingCartEntitySpec(
25-
EventSourcedBehaviorTestKit.config.withFallback(ConfigFactory.load)
26-
)
27-
28-
/**
29-
* CustomConfigShoppingCartEntitySpec demonstrates an alternative to ShoppingCartEntitySpec that
30-
* uses custom configuration instead of relying on `ConfigFactory.load`
31-
*/
32-
object CustomConfigShoppingCartEntitySpec {
33-
val testConfig =
34-
ConfigFactory.parseString("""
35-
|akka.actor {
36-
| serialization-bindings {
37-
| "com.example.shoppingcart.impl.ShoppingCart$CommandSerializable" = jackson-json
38-
| }
39-
|}
40-
|""".stripMargin)
41-
}
42-
43-
class CustomConfigShoppingCartEntitySpec
44-
extends AbstractShoppingCartEntitySpec(
45-
EventSourcedBehaviorTestKit.config.withFallback(CustomConfigShoppingCartEntitySpec.testConfig)
46-
)
47-
48-
object AbstractShoppingCartEntitySpec {
49-
private val userSerializationRegistry = ShoppingCartSerializerRegistry
50-
// This method is unexpected complexity in order to build a typed ActorSystem with
51-
// the user's `ShoppingCartSerializerRegistry` registered so that user messages can
52-
// still use Lagom's play-json serializers with Akka Persistence Typed.
53-
def typedActorSystem(name: String, config: Config): typed.ActorSystem[Nothing] = {
54-
val setup: ActorSystemSetup =
55-
ActorSystemSetup(
56-
BootstrapSetup(classLoader = Some(classOf[AbstractShoppingCartEntitySpec].getClassLoader), config = Some(config), None),
57-
JsonSerializerRegistry.serializationSetupFor(userSerializationRegistry)
58-
)
59-
import akka.actor.typed.scaladsl.adapter._
60-
ActorSystem(name, setup).toTyped
61-
}
62-
63-
}
64-
65-
abstract class AbstractShoppingCartEntitySpec(config: Config)
66-
extends ScalaTestWithActorTestKit(AbstractShoppingCartEntitySpec.typedActorSystem("ShoppingCartEntitySpec", config))
67-
with AnyWordSpecLike {
11+
extends ScalaTestWithActorTestKit(s"""
12+
|akka.persistence.journal.plugin = "akka.persistence.journal.inmem"
13+
|akka.persistence.snapshot-store.plugin = "akka.persistence.snapshot-store.local"
14+
|akka.persistence.snapshot-store.local.dir = "target/snapshot-${UUID
15+
.randomUUID()
16+
.toString}"
17+
|""".stripMargin)
18+
with AnyWordSpecLike
19+
with LogCapturing {
6820

6921
private def randomId(): String = UUID.randomUUID().toString
7022

7123
"ShoppingCart" must {
7224
"add an item" in {
73-
val entity = EventSourcedBehaviorTestKit[ShoppingCart.Command, ShoppingCart.Event, ShoppingCart](
74-
system,
75-
ShoppingCart(PersistenceId("ShoppingCart", randomId()))
76-
)
25+
val probe = createTestProbe[ShoppingCart.Confirmation]()
26+
val shoppingCart = spawn(ShoppingCart(PersistenceId("ShoppingCart", randomId())))
27+
shoppingCart ! ShoppingCart.AddItem(UUID.randomUUID().toString, 2, probe.ref)
28+
29+
probe.expectMessageType[ShoppingCart.Accepted]
30+
}
31+
32+
"remove an item" in {
33+
val probe = createTestProbe[ShoppingCart.Confirmation]()
34+
val shoppingCart = spawn(ShoppingCart(PersistenceId("ShoppingCart", randomId())))
35+
36+
// First add a item
37+
val itemId = randomId()
38+
shoppingCart ! ShoppingCart.AddItem(itemId, 2, probe.ref)
39+
probe.expectMessageType[ShoppingCart.Accepted]
40+
41+
// Then remove the item
42+
shoppingCart ! ShoppingCart.RemoveItem(itemId, probe.ref)
43+
probe.receiveMessage() match {
44+
case ShoppingCart.Accepted(summary) => summary.items.contains(itemId) shouldBe false
45+
case ShoppingCart.Rejected(reason) => fail(s"Message was rejected with reason: $reason")
46+
}
47+
}
48+
49+
"update item quantity" in {
50+
val probe = createTestProbe[ShoppingCart.Confirmation]()
51+
val shoppingCart = spawn(ShoppingCart(PersistenceId("ShoppingCart", randomId())))
52+
53+
// First add a item
54+
val itemId = randomId()
55+
shoppingCart ! ShoppingCart.AddItem(itemId, 2, probe.ref)
56+
probe.expectMessageType[ShoppingCart.Accepted]
57+
58+
// Update item quantity
59+
shoppingCart ! ShoppingCart.AdjustItemQuantity(itemId, 5, probe.ref)
60+
probe.receiveMessage() match {
61+
case ShoppingCart.Accepted(summary) => summary.items.get(itemId) shouldBe Some(5)
62+
case ShoppingCart.Rejected(reason) => fail(s"Message was rejected with reason: $reason")
63+
}
64+
}
65+
66+
"allow checking out" in {
67+
val probe = createTestProbe[ShoppingCart.Confirmation]()
68+
val shoppingCart = spawn(ShoppingCart(PersistenceId("ShoppingCart", randomId())))
69+
70+
// First add a item
71+
shoppingCart ! ShoppingCart.AddItem(randomId(), 2, probe.ref)
72+
probe.expectMessageType[ShoppingCart.Accepted]
73+
74+
// Checkout shopping cart
75+
shoppingCart ! ShoppingCart.Checkout(probe.ref)
76+
probe.receiveMessage() match {
77+
case ShoppingCart.Accepted(summary) => summary.checkedOut shouldBe true
78+
case ShoppingCart.Rejected(reason) => fail(s"Message was rejected with reason: $reason")
79+
}
80+
}
81+
82+
"allow getting shopping cart summary" in {
83+
val probeAdd = createTestProbe[ShoppingCart.Confirmation]()
84+
val shoppingCart = spawn(ShoppingCart(PersistenceId("ShoppingCart", randomId())))
85+
86+
// First add a item
87+
val itemId = randomId()
88+
shoppingCart ! ShoppingCart.AddItem(itemId, 2, probeAdd.ref)
89+
90+
// Get the summary
91+
// Use another probe since ShoppingCart.Get does not return a ShoppingCart.Confirmation
92+
val probeGet = createTestProbe[ShoppingCart.Summary]()
93+
shoppingCart ! ShoppingCart.Get(probeGet.ref)
94+
probeGet.receiveMessage().items.get(itemId) shouldBe Some(2)
95+
}
96+
97+
"fail when removing an item that isn't added" in {
98+
val probe = createTestProbe[ShoppingCart.Confirmation]()
99+
val shoppingCart = spawn(ShoppingCart(PersistenceId("ShoppingCart", randomId())))
100+
101+
// First add a item
102+
val itemId = randomId()
103+
shoppingCart ! ShoppingCart.AddItem(itemId, 2, probe.ref)
104+
probe.expectMessageType[ShoppingCart.Accepted]
105+
106+
// Removing is idempotent, so command will not be Rejected
107+
val toRemoveItemId = randomId()
108+
shoppingCart ! ShoppingCart.RemoveItem(toRemoveItemId, probe.ref)
109+
probe.receiveMessage() match {
110+
case ShoppingCart.Accepted(summary) => summary.items.get(itemId) shouldBe Some(2)
111+
case ShoppingCart.Rejected(reason) => fail(s"Message was rejected with reason: $reason")
112+
}
113+
}
114+
115+
"fail when adding a negative number of items" in {
116+
val probe = createTestProbe[ShoppingCart.Confirmation]()
117+
val shoppingCart = spawn(ShoppingCart(PersistenceId("ShoppingCart", randomId())))
118+
119+
val quantity = -2
120+
shoppingCart ! ShoppingCart.AddItem(randomId(), quantity, probe.ref)
121+
probe.expectMessage(ShoppingCart.Rejected("Quantity must be greater than zero"))
122+
}
123+
124+
"fail when adjusting item quantity to negative number" in {
125+
val probe = createTestProbe[ShoppingCart.Confirmation]()
126+
val shoppingCart = spawn(ShoppingCart(PersistenceId("ShoppingCart", randomId())))
127+
128+
// First add a item so it is possible to checkout
129+
val itemId = randomId()
130+
shoppingCart ! ShoppingCart.AddItem(itemId, 2, probe.ref)
131+
probe.expectMessageType[ShoppingCart.Accepted]
132+
133+
val quantity = -2
134+
shoppingCart ! ShoppingCart.AdjustItemQuantity(itemId, quantity, probe.ref)
135+
probe.expectMessage(ShoppingCart.Rejected("Quantity must be greater than zero"))
136+
}
137+
138+
"fail when adjusting quantity for an item that isn't added" in {
139+
val probe = createTestProbe[ShoppingCart.Confirmation]()
140+
val shoppingCart = spawn(ShoppingCart(PersistenceId("ShoppingCart", randomId())))
141+
142+
val itemId = randomId()
143+
shoppingCart ! ShoppingCart.AdjustItemQuantity(itemId, 2, probe.ref)
144+
probe.expectMessage(ShoppingCart.Rejected(s"Cannot adjust quantity for item '$itemId'. Item not present on cart"))
145+
}
146+
147+
"fail when adding an item to a checked out cart" in {
148+
val probe = createTestProbe[ShoppingCart.Confirmation]()
149+
val shoppingCart = spawn(ShoppingCart(PersistenceId("ShoppingCart", randomId())))
150+
151+
// First add a item so it is possible to checkout
152+
val itemId = randomId()
153+
shoppingCart ! ShoppingCart.AddItem(itemId, 2, probe.ref)
154+
probe.expectMessageType[ShoppingCart.Accepted]
155+
156+
// Then checkout the shopping cart
157+
shoppingCart ! ShoppingCart.Checkout(probe.ref)
158+
probe.expectMessageType[ShoppingCart.Accepted]
159+
160+
// Then fail when adding new items
161+
shoppingCart ! ShoppingCart.AddItem(randomId(), 2, probe.ref)
162+
probe.expectMessage(ShoppingCart.Rejected("Cannot add an item to a checked-out cart"))
163+
}
164+
165+
"fail when checking out twice" in {
166+
val probe = createTestProbe[ShoppingCart.Confirmation]()
167+
val shoppingCart = spawn(ShoppingCart(PersistenceId("ShoppingCart", randomId())))
168+
169+
// First add a item so it is possible to checkout
170+
val itemId = randomId()
171+
shoppingCart ! ShoppingCart.AddItem(itemId, 2, probe.ref)
172+
probe.expectMessageType[ShoppingCart.Accepted]
173+
174+
// Then checkout the shopping cart
175+
shoppingCart ! ShoppingCart.Checkout(probe.ref)
176+
probe.expectMessageType[ShoppingCart.Accepted]
177+
178+
// Then fail to checkout again
179+
shoppingCart ! ShoppingCart.Checkout(probe.ref)
180+
probe.expectMessage(ShoppingCart.Rejected("Cannot checkout a checked-out cart"))
181+
}
182+
183+
"fail when checking out an empty cart" in {
184+
val probe = createTestProbe[ShoppingCart.Confirmation]()
185+
val shoppingCart = spawn(ShoppingCart(PersistenceId("ShoppingCart", randomId())))
77186

78-
val result = entity.runCommand(AddItem("1", 1, _))
79-
result.reply shouldBe a[Confirmation]
187+
// Fail to checkout empty shopping cart
188+
shoppingCart ! ShoppingCart.Checkout(probe.ref)
189+
probe.expectMessage(ShoppingCart.Rejected("Cannot checkout an empty shopping cart"))
80190
}
81191
}
82192
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package com.example.shoppingcart.impl
2+
3+
import java.util.UUID
4+
5+
import akka.actor.ActorSystem
6+
import akka.actor.BootstrapSetup
7+
import akka.actor.setup.ActorSystemSetup
8+
import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit
9+
import akka.actor.typed
10+
import akka.persistence.testkit.scaladsl.EventSourcedBehaviorTestKit
11+
import akka.persistence.typed.PersistenceId
12+
import com.example.shoppingcart.impl.ShoppingCart.AddItem
13+
import com.example.shoppingcart.impl.ShoppingCart.Confirmation
14+
import com.lightbend.lagom.scaladsl.playjson.JsonSerializerRegistry
15+
import com.typesafe.config.Config
16+
import com.typesafe.config.ConfigFactory
17+
import org.scalatest.wordspec.AnyWordSpecLike
18+
19+
20+
/**
21+
* ConfigFactory.load will read the serialization settings from application.conf
22+
*/
23+
class ShoppingCartEntityTypedTestkitSpec
24+
extends AbstractShoppingCartEntityTypedTestkitSpec(
25+
EventSourcedBehaviorTestKit.config.withFallback(ConfigFactory.load)
26+
)
27+
28+
/**
29+
* CustomConfigShoppingCartEntityTypedTestkitSpec demonstrates an alternative to ShoppingCartEntityTypedTestkitSpec that
30+
* uses custom configuration instead of relying on `ConfigFactory.load`
31+
*/
32+
object CustomConfigShoppingCartEntityTypedTestkitSpec {
33+
val testConfig =
34+
ConfigFactory.parseString("""
35+
|akka.actor {
36+
| serialization-bindings {
37+
| "com.example.shoppingcart.impl.ShoppingCart$CommandSerializable" = jackson-json
38+
| }
39+
|}
40+
|""".stripMargin)
41+
}
42+
43+
class CustomConfigShoppingCartEntityTypedTestkitSpec
44+
extends AbstractShoppingCartEntityTypedTestkitSpec(
45+
EventSourcedBehaviorTestKit.config.withFallback(CustomConfigShoppingCartEntityTypedTestkitSpec.testConfig)
46+
)
47+
48+
object AbstractShoppingCartEntityTypedTestkitSpec {
49+
private val userSerializationRegistry = ShoppingCartSerializerRegistry
50+
// This method is unexpected complexity in order to build a typed ActorSystem with
51+
// the user's `ShoppingCartSerializerRegistry` registered so that user messages can
52+
// still use Lagom's play-json serializers with Akka Persistence Typed.
53+
def typedActorSystem(name: String, config: Config): typed.ActorSystem[Nothing] = {
54+
val setup: ActorSystemSetup =
55+
ActorSystemSetup(
56+
BootstrapSetup(classLoader = Some(classOf[AbstractShoppingCartEntityTypedTestkitSpec].getClassLoader), config = Some(config), None),
57+
JsonSerializerRegistry.serializationSetupFor(userSerializationRegistry)
58+
)
59+
import akka.actor.typed.scaladsl.adapter._
60+
ActorSystem(name, setup).toTyped
61+
}
62+
63+
}
64+
65+
abstract class AbstractShoppingCartEntityTypedTestkitSpec(config: Config)
66+
extends ScalaTestWithActorTestKit(AbstractShoppingCartEntityTypedTestkitSpec.typedActorSystem("ShoppingCartEntityTypedTestkitSpec", config))
67+
with AnyWordSpecLike {
68+
69+
private def randomId(): String = UUID.randomUUID().toString
70+
71+
"ShoppingCart" must {
72+
"add an item" in {
73+
val entity = EventSourcedBehaviorTestKit[ShoppingCart.Command, ShoppingCart.Event, ShoppingCart](
74+
system,
75+
ShoppingCart(PersistenceId("ShoppingCart", randomId()))
76+
)
77+
78+
val result = entity.runCommand(AddItem("1", 1, _))
79+
result.reply shouldBe a[Confirmation]
80+
}
81+
}
82+
}

0 commit comments

Comments
 (0)