Skip to content

Commit

Permalink
Implement removeProduct method of ProductStore
Browse files Browse the repository at this point in the history
  • Loading branch information
ekuzmichev committed Sep 18, 2024
1 parent 9d2ae15 commit d341fb1
Show file tree
Hide file tree
Showing 7 changed files with 229 additions and 3 deletions.
5 changes: 5 additions & 0 deletions src/main/scala/store/CacheProductStore.scala
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,8 @@ class CacheProductStore(decoratee: ProductStore, cacheStateRepository: CacheStat

override def addProduct(sourceId: SourceId, product: Product): Task[Boolean] =
decoratee.addProduct(sourceId, product) <* replaceStateInCache()

override def removeProduct(sourceId: SourceId, productId: ProductId): Task[Boolean] =
decoratee.removeProduct(sourceId, productId) <* replaceStateInCache()


3 changes: 3 additions & 0 deletions src/main/scala/store/InMemoryProductStore.scala
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ class InMemoryProductStore(productStateRef: Ref[ProductState]) extends ProductSt
override def checkHasProductId(sourceId: SourceId, productId: ProductId): Task[Boolean] =
productStateRef.get.map(_.get(sourceId).exists(_.products.exists(_.id == productId)))

override def removeProduct(sourceId: SourceId, productId: ProductId): Task[Boolean] =
doUpdate(sourceId, sourceState => sourceState.copy(products = sourceState.products.filter(_.id != productId)))

private def doUpdateProductCandidate(
sourceId: SourceId,
maybeProductCandidate: Option[ProductCandidate]
Expand Down
1 change: 1 addition & 0 deletions src/main/scala/store/ProductStore.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ trait ProductStore:
def updateProductCandidate(sourceId: SourceId, productCandidate: ProductCandidate): Task[Boolean]
def resetProductCandidate(sourceId: SourceId): Task[Boolean]
def addProduct(sourceId: SourceId, product: ProductStore.Product): Task[Boolean]
def removeProduct(sourceId: SourceId, productId: ProductId): Task[Boolean]

object ProductStore:
case class SourceId(userName: UserName, chatId: ChatId) extends NamedToString
Expand Down
85 changes: 85 additions & 0 deletions src/test/scala/KeyboardBotTestApp.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package ru.ekuzmichev

import org.telegram.telegrambots.client.okhttp.OkHttpTelegramClient
import org.telegram.telegrambots.longpolling.TelegramBotsLongPollingApplication
import org.telegram.telegrambots.longpolling.util.LongPollingSingleThreadUpdateConsumer
import org.telegram.telegrambots.meta.api.methods.send.SendMessage
import org.telegram.telegrambots.meta.api.objects.Update
import org.telegram.telegrambots.meta.api.objects.replykeyboard.InlineKeyboardMarkup
import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.{InlineKeyboardButton, InlineKeyboardRow}
import org.telegram.telegrambots.meta.generics.TelegramClient

object KeyboardBotTestApp extends App:
val botToken = "7214794484:AAEmry5Wwg-uW-bCa4QbK6dUZFzM5VdRbL4"

val botsApplication = new TelegramBotsLongPollingApplication()
botsApplication.registerBot(botToken, new KeyboardTestingBot(new OkHttpTelegramClient(botToken)))

println("MyAmazingBot successfully started!")

class KeyboardTestingBot(telegramClient: TelegramClient) extends LongPollingSingleThreadUpdateConsumer:
override def consume(update: Update): Unit =
println { update }
if update.getMessage != null then
val messageText = "Fuck!"
val chatId = update.getMessage.getChatId

val inlineKeyboardButtons: Seq[InlineKeyboardButton] = (1 to 1).map(index =>
InlineKeyboardButton
.builder()
.text(s"$index umnyy-robot-pylesos-red-solution-rv-rl6000s-wi-fi-suhaya-i-vlazhnaya-uborka-879677278")
.callbackData(index.toString)
.build()
)

val inlineKeyboardRow: InlineKeyboardRow = new InlineKeyboardRow(
inlineKeyboardButtons*
)

val inlineKeyboardMarkup: InlineKeyboardMarkup = InlineKeyboardMarkup
.builder()
.keyboardRow(inlineKeyboardRow)
.keyboardRow(inlineKeyboardRow)
.keyboardRow(inlineKeyboardRow)
.keyboardRow(inlineKeyboardRow)
.keyboardRow(inlineKeyboardRow)
.keyboardRow(inlineKeyboardRow)
.keyboardRow(inlineKeyboardRow)
.keyboardRow(new InlineKeyboardRow(
InlineKeyboardButton
.builder()
.text("More")
.callbackData("more")
.build()
))
.build()

val message: SendMessage = SendMessage
.builder()
.text("Hello you!")
.chatId(chatId)
.replyMarkup(inlineKeyboardMarkup)
.build()

telegramClient.execute(message)
else if update.getCallbackQuery != null then
println {
update.getCallbackQuery.getMessage.getChatId
}
println {
update.getCallbackQuery.getId
}
println {
update.getCallbackQuery.getData
}
println {
update.getCallbackQuery.getFrom
}

val message: SendMessage = SendMessage
.builder()
.text(update.getCallbackQuery.getData)
.chatId(update.getCallbackQuery.getMessage.getChatId)
.build()

telegramClient.execute(message)
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ package product
import util.ozon.OzonShortUrlResolverImpl

import net.ruippeixotog.scalascraper.browser.JsoupBrowser
import zio.Scope
import zio.test.*
import zio.test.Assertion.*

object OzonProductIdParserIntegrationTest extends ZIOSpecDefault:
def spec: Spec[Any, Throwable] =
override def spec: Spec[TestEnvironment & Scope, Any] =
suite("OzonProductIdParser.parse")(
test("parse Ozon product id from Ozon URL 'shared' from browser") {
val parser = makeProductIdParser
Expand Down
130 changes: 130 additions & 0 deletions src/test/scala/store/InMemoryProductStoreTest.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
package ru.ekuzmichev
package store

import common.ProductId
import store.InMemoryProductStore.ProductState
import store.ProductStore.{Product, SourceId, SourceState}

import zio.test.*
import zio.test.Assertion.*
import zio.{Ref, Scope, UIO}

object InMemoryProductStoreTest extends ZIOSpecDefault:
override def spec: Spec[TestEnvironment with Scope, Any] =
suite("InMemoryProductStore")(
test("preInitialize should fill state with initial data") {
import Fixtures.sourceStatesBySortId
for
productStore <- makeProductStore()
stateBeforeInitialization <- productStore.readAll()
_ <- productStore.preInitialize(sourceStatesBySortId)
stateAfterInitialization <- productStore.readAll()
yield assertTrue(stateBeforeInitialization.isEmpty) &&
assert(stateAfterInitialization)(hasSameElements(sourceStatesBySortId))
},
test("checkInitialized should return true if sourceId exists and false otherwise") {
import Fixtures.{bobSourceId, fritzSourceId, pamelaSourceId}
for
productStore <- makePreInitializedProductStore()
bobInitialized <- productStore.checkInitialized(bobSourceId)
fritzInitialized <- productStore.checkInitialized(fritzSourceId)
pamelaInitialized <- productStore.checkInitialized(pamelaSourceId)
yield assertTrue(bobInitialized, fritzInitialized, !pamelaInitialized)
},
test("emptyState should remove all products for sourceId") {
import Fixtures.bobSourceId
for
productStore <- makePreInitializedProductStore()
_ <- productStore.emptyState(bobSourceId)
stateAfterEmptiness <- productStore.readAll()
yield assertTrue(stateAfterEmptiness(bobSourceId).products.isEmpty)
},
test("clearState should remove whole state for sourceId") {
import Fixtures.bobSourceId
for
productStore <- makePreInitializedProductStore()
_ <- productStore.clearState(bobSourceId)
stateAfterClearance <- productStore.readAll()
yield assertTrue(!stateAfterClearance.contains(bobSourceId))
},
test("readSourceState should read state for sourceId") {
import Fixtures.{bobSourceId, bobSourceState}
for
productStore <- makePreInitializedProductStore()
readBobSourceState <- productStore.readSourceState(bobSourceId)
yield assertTrue(readBobSourceState.get == bobSourceState)
},
test("addProduct should add product to product list of sourceId if sourceId exists") {
import Fixtures.{bobSourceId, pamelaSourceId}
for
productStore <- makePreInitializedProductStore()
newProduct = Product("blue-water", 1500)
bobProductAdded <- productStore.addProduct(bobSourceId, newProduct)
readBobSourceState <- productStore.readSourceState(bobSourceId)
pamelaProductAdded <- productStore.addProduct(pamelaSourceId, newProduct)
yield assertTrue(bobProductAdded, readBobSourceState.get.products.contains(newProduct), !pamelaProductAdded)
},
test("checkHasProductId should return true if product with id exists and false otherwise") {
import Fixtures.{bobSourceId, redKettleProductId, whiteLampProductId}
for
productStore <- makePreInitializedProductStore()
hasRedKettleProduct <- productStore.checkHasProductId(bobSourceId, redKettleProductId)
hasWhiteLampProduct <- productStore.checkHasProductId(bobSourceId, whiteLampProductId)
yield assertTrue(hasRedKettleProduct, !hasWhiteLampProduct)
},
test("removeProduct should remove product from product list of sourceId if sourceId exists") {
import Fixtures.{bobSourceId, pamelaSourceId, redKettleProductId}
for
productStore <- makePreInitializedProductStore()
bobProductRemoved <- productStore.removeProduct(bobSourceId, redKettleProductId)
readBobSourceState <- productStore.readSourceState(bobSourceId)
pamelaProductRemoved <- productStore.removeProduct(pamelaSourceId, redKettleProductId)
yield assertTrue(
bobProductRemoved,
!readBobSourceState.get.products.exists(_.id == redKettleProductId),
!pamelaProductRemoved
)
}
)

private def makePreInitializedProductStore(): UIO[InMemoryProductStore] =
import Fixtures.sourceStatesBySortId
makeProductStore().tap(_.preInitialize(sourceStatesBySortId).ignore)

private def makeProductStore(): UIO[InMemoryProductStore] =
for productStateRef <- Ref.make(ProductState.empty)
yield new InMemoryProductStore(productStateRef)

private object Fixtures:
val bobSourceId: SourceId = SourceId("bob", "123")
val fritzSourceId: SourceId = SourceId("fritz", "456")
val pamelaSourceId: SourceId = SourceId("pamela", "789")

val redKettleProductId: ProductId = "red-kettle"
val brownPenProductId: ProductId = "brown-pen"
val yellowPillowProductId: ProductId = "yellow-pillow"
val darkToyProductId: ProductId = "dark-toy"
val greenGrassProductId: ProductId = "green-grass"
val whiteLampProductId: ProductId = "white-lamp"

val bobSourceState: SourceState = SourceState(
products = Seq(
Product(redKettleProductId, 1000),
Product(brownPenProductId, 300)
),
maybeProductCandidate = None
)

val fritzSourceState: SourceState = SourceState(
products = Seq(
Product(yellowPillowProductId, 2000),
Product(darkToyProductId, 300),
Product(greenGrassProductId, 100)
),
maybeProductCandidate = None
)

val sourceStatesBySortId: Map[SourceId, SourceState] = Map(
bobSourceId -> bobSourceState,
fritzSourceId -> fritzSourceState
)
5 changes: 3 additions & 2 deletions src/test/scala/util/uri/UrlExtractionUtilsTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ package ru.ekuzmichev
package util.uri

import io.lemonlabs.uri.{QueryString, Url}
import zio.test.{Spec, ZIOSpecDefault, assertTrue}
import zio.Scope
import zio.test.{Spec, TestEnvironment, ZIOSpecDefault, assertTrue}

object UrlExtractionUtilsTest extends ZIOSpecDefault:
def spec: Spec[Any, Throwable] =
override def spec: Spec[TestEnvironment & Scope, Any] =
suite("UrlExtractionUtils.extractUrl")(
test("should extract URL from text containing it") {
for maybeUrl <- UrlExtractionUtils.extractUrl(
Expand Down

0 comments on commit d341fb1

Please sign in to comment.