Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 deletions backend/core/src/core/actions/chat/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,19 +107,19 @@ def remove_group_if_empty(self, group_id: int) -> None:
to be removed.
"""
try:
group_removed = self.telegram_chat_rule_group_service.delete(
chat_id=self.chat.id, group_id=group_id
)
if group_removed:
logger.info(
f"Deleted rule group {group_id!r} for chat {self.chat.id!r} as it had no rules left."
)
else:
logger.warning(
f"Group {group_id!r} was not deleted as it was not found."
with self.db_session.begin_nested():
group_removed = self.telegram_chat_rule_group_service.delete(
chat_id=self.chat.id, group_id=group_id
)
if group_removed:
logger.info(
f"Deleted rule group {group_id!r} for chat {self.chat.id!r} as it had no rules left."
)
else:
logger.warning(
f"Group {group_id!r} was not deleted as it was not found."
)
except IntegrityError:
self.db_session.rollback()
logger.debug(f"Group {group_id!r} is not empty")

return None
Expand Down
3 changes: 1 addition & 2 deletions backend/core/src/core/actions/chat/rule/blockchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -619,7 +619,7 @@ def update(
self.refresh_chat_floor_price()
return ChatEligibilityRuleDTO.from_toncoin_rule(updated_rule)

def delete(self, rule_id: int) -> None:
async def delete(self, rule_id: int) -> None:
try:
group_id = self.telegram_chat_toncoin_service.get(
rule_id, chat_id=self.chat.id
Expand All @@ -631,6 +631,5 @@ def delete(self, rule_id: int) -> None:
)
self.telegram_chat_toncoin_service.delete(rule_id, chat_id=self.chat.id)
logger.info(f"Deleted chat TON rule {rule_id!r}")

self.refresh_chat_floor_price()
self.remove_group_if_empty(group_id=group_id)
14 changes: 14 additions & 0 deletions backend/tests/factories/nft.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import factory
from core.models.blockchain import NFTCollection
from tests.factories.base import BaseSQLAlchemyModelFactory


class NFTCollectionFactory(BaseSQLAlchemyModelFactory):
class Meta:
model = NFTCollection
sqlalchemy_session_persistence = "flush"

address = factory.Faker("pystr", min_chars=65, max_chars=65, prefix="0:")
name = factory.Faker("word")
description = factory.Faker("text")
is_enabled = True
1 change: 0 additions & 1 deletion backend/tests/factories/rule/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ class Meta:
abstract = True
sqlalchemy_session_persistence = "flush"

id = factory.Sequence(lambda n: n + 1)
group_id = factory.SelfAttribute("group.id")
group = factory.SubFactory(
"tests.factories.rule.group.TelegramChatRuleGroupFactory"
Expand Down
41 changes: 41 additions & 0 deletions backend/tests/factories/rule/blockchain.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import factory

from core.models.rule import (
TelegramChatJetton,
TelegramChatNFTCollection,
TelegramChatToncoin,
)
from tests.factories.jetton import JettonFactory
from tests.factories.nft import NFTCollectionFactory
from tests.factories.rule.base import (
TelegramChatRuleBaseFactory,
TelegramChatThresholdRuleMixin,
)


class TelegramChatJettonRuleFactory(
TelegramChatRuleBaseFactory, TelegramChatThresholdRuleMixin
):
class Meta:
model = TelegramChatJetton

address = factory.SelfAttribute("jetton.address")
jetton = factory.SubFactory(JettonFactory)


class TelegramChatNFTCollectionRuleFactory(
TelegramChatRuleBaseFactory, TelegramChatThresholdRuleMixin
):
class Meta:
model = TelegramChatNFTCollection

address = factory.SelfAttribute("nft_collection.address")
nft_collection = factory.SubFactory(NFTCollectionFactory)
asset = None


class TelegramChatToncoinRuleFactory(
TelegramChatRuleBaseFactory, TelegramChatThresholdRuleMixin
):
class Meta:
model = TelegramChatToncoin
124 changes: 124 additions & 0 deletions backend/tests/unit/core/actions/chat/rule/test_blockchain_delete.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import pytest
from sqlalchemy.orm import Session

from core.actions.chat.base import ManagedChatBaseAction
from core.actions.chat.rule.blockchain import (
TelegramChatJettonAction,
TelegramChatNFTCollectionAction,
TelegramChatToncoinAction,
)
from core.models.rule import (
TelegramChatJetton,
TelegramChatNFTCollection,
TelegramChatToncoin,
TelegramChatRuleGroup,
TelegramChatRuleBase,
)
from tests.factories.rule.base import TelegramChatRuleBaseFactory
from tests.factories.rule.group import TelegramChatRuleGroupFactory
from tests.factories.rule.blockchain import (
TelegramChatJettonRuleFactory,
TelegramChatNFTCollectionRuleFactory,
TelegramChatToncoinRuleFactory,
)
from tests.factories import UserFactory
from tests.fixtures.action import ChatManageActionFactory


@pytest.mark.parametrize(
("action_cls", "factory_cls", "model_cls"),
[
(TelegramChatJettonAction, TelegramChatJettonRuleFactory, TelegramChatJetton),
(
TelegramChatNFTCollectionAction,
TelegramChatNFTCollectionRuleFactory,
TelegramChatNFTCollection,
),
(
TelegramChatToncoinAction,
TelegramChatToncoinRuleFactory,
TelegramChatToncoin,
),
],
)
@pytest.mark.asyncio
async def test_delete_rule__last_in_group__group_removed(
db_session: Session,
mocked_managed_chat_action_factory: ChatManageActionFactory,
action_cls: type[ManagedChatBaseAction],
factory_cls: type[TelegramChatRuleBaseFactory],
model_cls: type[TelegramChatRuleBase],
) -> None:
group = TelegramChatRuleGroupFactory.with_session(db_session).create()
rule = factory_cls.with_session(db_session).create(group=group, chat=group.chat)
requestor = UserFactory.with_session(db_session).create()

action = mocked_managed_chat_action_factory(
action_cls=action_cls,
db_session=db_session,
chat_slug=rule.chat.slug,
requestor=requestor,
)

await action.delete(rule_id=rule.id)

assert db_session.query(model_cls).first() is None, "The rule should be deleted."
assert (
db_session.query(TelegramChatRuleGroup).filter_by(id=group.id).first() is None
), "The group should be deleted."


@pytest.mark.parametrize(
("action_cls", "factory_cls", "model_cls"),
[
(TelegramChatJettonAction, TelegramChatJettonRuleFactory, TelegramChatJetton),
(
TelegramChatNFTCollectionAction,
TelegramChatNFTCollectionRuleFactory,
TelegramChatNFTCollection,
),
(
TelegramChatToncoinAction,
TelegramChatToncoinRuleFactory,
TelegramChatToncoin,
),
],
)
@pytest.mark.asyncio
async def test_delete_rule__other_rules_exist__group_retained(
db_session: Session,
mocked_managed_chat_action_factory: ChatManageActionFactory,
action_cls: type[ManagedChatBaseAction],
factory_cls: type[TelegramChatRuleBaseFactory],
model_cls: type[TelegramChatRuleBase],
) -> None:
group = TelegramChatRuleGroupFactory.with_session(db_session).create()
group_id = (
group.id
) # Store ID to avoid accessing stale object after commit/rollback
rule_to_delete = factory_cls.with_session(db_session).create(
group=group, chat=group.chat
)
rule_to_delete_id = rule_to_delete.id
# Create another rule in the same group
factory_cls.with_session(db_session).create(group=group, chat=group.chat)

requestor = UserFactory.with_session(db_session).create()

action = mocked_managed_chat_action_factory(
action_cls=action_cls,
db_session=db_session,
chat_slug=group.chat.slug,
requestor=requestor,
)

await action.delete(rule_id=rule_to_delete_id)

assert (
db_session.query(model_cls).filter_by(id=rule_to_delete_id).first() is None
), "The specific rule should be deleted."
assert (
db_session.query(TelegramChatRuleGroup).filter_by(id=group_id).first()
is not None
), "The group should NOT be deleted."
assert db_session.query(model_cls).count() == 1, "One rule should remain."
2 changes: 1 addition & 1 deletion docker-compose.dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ services:
volumes:
- ./backend/api:/app/api
- ./backend/core:/app/core
command: [ "uvicorn", "api.app:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "4", "--reload" ]
command: [ "uvicorn", "api.app:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "4", "--reload", "--log-level=info" ]


nginx:
Expand Down