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
69 changes: 69 additions & 0 deletions backend/tests/apps/slack/admin/member_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
from unittest.mock import MagicMock

import pytest
from django.contrib import messages
from django.contrib.admin.sites import AdminSite

from apps.slack.admin.member import MemberAdmin
from apps.slack.models.member import Member


@pytest.fixture
def admin_instance():
return MemberAdmin(model=Member, admin_site=AdminSite())


class TestMemberAdmin:
def test_approve_suggested_users_success(self, admin_instance):
request = MagicMock()
mock_suggested_user = MagicMock()
mock_member = MagicMock()

mock_member.suggested_users.all.return_value.count.return_value = 1
mock_member.suggested_users.all.return_value.first.return_value = mock_suggested_user

admin_instance.message_user = MagicMock()
queryset = [mock_member]

admin_instance.approve_suggested_users(request, queryset)

assert mock_member.user == mock_suggested_user
mock_member.save.assert_called_once()
admin_instance.message_user.assert_called_with(
request, pytest.approx(f" assigned user for {mock_member}."), messages.SUCCESS
)

def test_approve_suggested_users_multiple_error(self, admin_instance):
request = MagicMock()
mock_member = MagicMock()

mock_member.suggested_users.all.return_value.count.return_value = 2

admin_instance.message_user = MagicMock()
queryset = [mock_member]

admin_instance.approve_suggested_users(request, queryset)

mock_member.save.assert_not_called()
expected_message = (
f"Error: Multiple suggested users found for {mock_member}. "
f"Only one user can be assigned due to the one-to-one constraint."
)

admin_instance.message_user.assert_called_with(request, expected_message, messages.ERROR)

def test_approve_suggested_users_none_warning(self, admin_instance):
request = MagicMock()
mock_member = MagicMock()

mock_member.suggested_users.all.return_value.count.return_value = 0

admin_instance.message_user = MagicMock()
queryset = [mock_member]

admin_instance.approve_suggested_users(request, queryset)

mock_member.save.assert_not_called()
admin_instance.message_user.assert_called_with(
request, f"No suggested users found for {mock_member}.", messages.WARNING
)
99 changes: 99 additions & 0 deletions backend/tests/apps/slack/commands/command_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
from pathlib import Path
from unittest.mock import MagicMock, patch

import pytest

from apps.slack.blocks import DIVIDER, SECTION_BREAK
from apps.slack.commands.command import CommandBase


class Command(CommandBase):
pass


class TestCommandBase:
@pytest.fixture
def command_instance(self):
return Command()

@pytest.fixture
def mock_command_payload(self):
return {"user_id": "U123ABC"}

@patch("apps.slack.commands.command.logger")
@patch("apps.slack.commands.command.SlackConfig")
def test_configure_commands_when_app_is_none(self, mock_slack_config, mock_logger):
"""Tests that a warning is logged if the Slack app is not configured."""
mock_slack_config.app = None
CommandBase.configure_commands()
mock_logger.warning.assert_called_once()

def test_command_name_property(self, command_instance):
"""Tests that the command_name is derived correctly from the class name."""
assert command_instance.command_name == "/command"

def test_template_path_property(self, command_instance):
"""Tests that the template_path is derived correctly."""
assert command_instance.template_path == Path("commands/command.jinja")

@patch("apps.slack.commands.command.env")
def test_template_property(self, mock_jinja_env, command_instance):
"""Tests that the correct template is requested from the jinja environment."""
_ = command_instance.template

mock_jinja_env.get_template.assert_called_once_with("commands/command.jinja")

def test_render_blocks(self, command_instance):
"""Tests that the render_blocks method correctly parses rendered text into blocks."""
test_string = f"Hello World{SECTION_BREAK}{DIVIDER}{SECTION_BREAK}Welcome to Nest"

with patch.object(command_instance, "render_text", return_value=test_string):
blocks = command_instance.render_blocks(command={})

assert len(blocks) == 3
assert blocks[0]["text"]["text"] == "Hello World"
assert blocks[1]["type"] == "divider"
assert blocks[2]["text"]["text"] == "Welcome to Nest"

def test_handler_success(self, settings, command_instance, mock_command_payload):
"""Tests the successful path of the command handler."""
settings.SLACK_COMMANDS_ENABLED = True
ack = MagicMock()
mock_client = MagicMock()
mock_client.conversations_open.return_value = {"channel": {"id": "D123XYZ"}}

with patch.object(command_instance, "render_blocks", return_value=[{"type": "section"}]):
command_instance.handler(ack=ack, command=mock_command_payload, client=mock_client)

ack.assert_called_once()
mock_client.conversations_open.assert_called_once_with(users="U123ABC")
mock_client.chat_postMessage.assert_called_once()
assert mock_client.chat_postMessage.call_args[1]["channel"] == "D123XYZ"

def test_handler_api_error(self, mocker, settings, command_instance, mock_command_payload):
"""Tests that an exception during API calls is caught and logged."""
settings.SLACK_COMMANDS_ENABLED = True
mock_logger = mocker.patch("apps.slack.commands.command.logger")
ack = MagicMock()
mock_client = MagicMock()
mock_client.chat_postMessage.side_effect = [Exception("API Error"), {"ok": True}]
mocker.patch.object(command_instance, "render_blocks", return_value=[{"type": "section"}])
command_instance.handler(ack=ack, command=mock_command_payload, client=mock_client)
ack.assert_called_once()
mock_logger.exception.assert_called_once()
# Verify retry occurred and eventually succeeded
assert mock_client.chat_postMessage.call_count == 2
mock_logger.exception.assert_called_with(
"Failed to handle command '%s'", command_instance.command_name
)

def test_handler_when_commands_disabled(
self, settings, command_instance, mock_command_payload
):
"""Tests that no message is sent when commands are disabled."""
settings.SLACK_COMMANDS_ENABLED = False
ack = MagicMock()
mock_client = MagicMock()
command_instance.handler(ack=ack, command=mock_command_payload, client=mock_client)
ack.assert_called_once()
mock_client.chat_postMessage.assert_not_called()
67 changes: 67 additions & 0 deletions backend/tests/apps/slack/common/handlers/users_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import pytest

from apps.slack.common.handlers.users import get_blocks
from apps.slack.common.presentation import EntityPresentation


@pytest.fixture
def mock_users_data():
return {
"hits": [
{
"idx_name": "John Doe",
"idx_login": "johndoe",
"idx_url": "https://github.com/johndoe",
"idx_bio": "A passionate developer changing the world.",
"idx_location": "San Francisco",
"idx_company": "OWASP",
"idx_followers_count": 100,
"idx_following_count": 50,
"idx_public_repositories_count": 10,
}
],
"nbPages": 3,
}


class TestGetUsersBlocks:
def test_get_blocks_no_results(self, mocker):
"""Tests that a "No users found" message is returned when search results are empty."""
mock_get_users = mocker.patch("apps.github.index.search.user.get_users")
mock_get_users.return_value = {"hits": [], "nbPages": 0}
blocks = get_blocks(search_query="nonexistent")
assert len(blocks) == 1
assert "No users found for `nonexistent`" in blocks[0]["text"]["text"]

def test_get_blocks_with_results(self, mocker, mock_users_data):
"""Tests the happy path, ensuring user data is formatted correctly into blocks."""
mocker.patch("apps.github.index.search.user.get_users", return_value=mock_users_data)
blocks = get_blocks(search_query="john")
assert len(blocks) > 1
user_block_text = blocks[0]["text"]["text"]
assert "1. <https://github.com/johndoe|*John Doe*>" in user_block_text
assert "Company: OWASP" in user_block_text
assert "Location: San Francisco" in user_block_text
assert "Followers: 100" in user_block_text
assert "A passionate developer" in user_block_text

@pytest.mark.parametrize(
("include_pagination", "should_call_pagination"),
[
(True, True),
(False, False),
],
)
def test_get_blocks_pagination_logic(
self, mocker, mock_users_data, include_pagination, should_call_pagination
):
mocker.patch("apps.github.index.search.user.get_users", return_value=mock_users_data)
mock_get_pagination = mocker.patch(
"apps.slack.common.handlers.users.get_pagination_buttons"
)
presentation = EntityPresentation(include_pagination=include_pagination)
get_blocks(presentation=presentation)
if should_call_pagination:
mock_get_pagination.assert_called_once_with("users", 1, 2)
else:
mock_get_pagination.assert_not_called()
Loading