Skip to content

Commit 24a5644

Browse files
committed
fixed tests
1 parent 0c604bc commit 24a5644

File tree

2 files changed

+29
-236
lines changed

2 files changed

+29
-236
lines changed

backend/apps/github/management/commands/github_match_users.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
from django.contrib.contenttypes.models import ContentType
44
from django.core.management.base import BaseCommand
5-
from django.db import transaction
65
from thefuzz import fuzz
76

87
from apps.github.models.user import User
@@ -31,7 +30,6 @@ def add_arguments(self, parser):
3130
help="Threshold for fuzzy matching (0-100)",
3231
)
3332

34-
@transaction.atomic
3533
def handle(self, *_args, **kwargs):
3634
model_name = kwargs["model_name"].lower()
3735
threshold = max(0, min(kwargs["threshold"], 100))

backend/tests/apps/github/management/commands/github_match_users_test.py

Lines changed: 29 additions & 234 deletions
Original file line numberDiff line numberDiff line change
@@ -10,35 +10,37 @@
1010

1111
@pytest.fixture
1212
def command():
13-
"""Return a command instance."""
14-
return Command()
13+
"""Return a command instance with a mocked stdout."""
14+
cmd = Command()
15+
cmd.stdout = MagicMock()
16+
return cmd
1517

1618

1719
class TestGithubMatchUsersCommand:
18-
"""Test suite for the github_match_users command."""
20+
"""Test suite for the command's setup and helper methods."""
1921

2022
def test_command_help_text(self, command):
21-
"""Test that the command has the correct help text."""
23+
"""Test that the command has the new, correct help text."""
2224
assert (
2325
command.help
24-
== "Match leaders or Slack members with GitHub users using exact and fuzzy matching."
26+
== "Matches entity leader names with GitHub Users and creates EntityMember records."
2527
)
2628

2729
def test_command_inheritance(self, command):
2830
"""Test that the command inherits from BaseCommand."""
2931
assert isinstance(command, BaseCommand)
3032

3133
def test_add_arguments(self, command):
32-
"""Test that the command adds the correct arguments."""
34+
"""Test that the command adds the correct arguments for the new version."""
3335
parser = MagicMock()
3436
command.add_arguments(parser)
3537

3638
assert parser.add_argument.call_count == 2
3739
parser.add_argument.assert_any_call(
3840
"model_name",
3941
type=str,
40-
choices=("chapter", "committee", "member", "project"),
41-
help="Model name to process: chapter, committee, project, or member",
42+
choices=("chapter", "committee", "project", "all"),
43+
help="Model to process: chapter, committee, project, or all.",
4244
)
4345
parser.add_argument.assert_any_call(
4446
"--threshold",
@@ -67,246 +69,39 @@ def test_is_valid_user(self, command, login, name, expected):
6769
assert command._is_valid_user(login, name) == expected
6870

6971

70-
class TestProcessLeaders:
71-
"""Test suite for the process_leaders method."""
72-
73-
@pytest.fixture
74-
def command(self):
75-
"""Return a command instance."""
76-
command = Command()
77-
command.stdout = MagicMock()
78-
79-
return command
72+
class TestFindUserMatches:
73+
"""Test suite for the _find_user_matches helper method."""
8074

8175
@pytest.fixture
8276
def mock_users(self):
8377
"""Return a dictionary of mock users."""
84-
return {
85-
1: {"id": 1, "login": "john_doe", "name": "John Doe"},
86-
2: {"id": 2, "login": "jane_doe", "name": "Jane Doe"},
87-
3: {"id": 3, "login": "peter_jones", "name": "Peter Jones"},
88-
4: {"id": 4, "login": "testuser", "name": "Test User"},
89-
}
90-
91-
def test_no_leaders(self, command):
92-
"""Test with no leaders provided."""
93-
exact, fuzzy, unmatched = command.process_leaders([], 75, {})
94-
95-
assert exact == []
96-
assert fuzzy == []
97-
assert unmatched == []
78+
return [
79+
{"id": 1, "login": "john_doe", "name": "John Doe"},
80+
{"id": 2, "login": "jane_doe", "name": "Jane Doe"},
81+
{"id": 3, "login": "peter_jones", "name": "Peter Jones"},
82+
]
9883

9984
def test_exact_match(self, command, mock_users):
100-
"""Test exact matching."""
85+
"""Test exact matching by login and name."""
10186
leaders_raw = ["john_doe", "Jane Doe"]
102-
exact, fuzzy, unmatched = command.process_leaders(leaders_raw, 75, mock_users)
87+
matches = command._find_user_matches(leaders_raw, mock_users, 90)
10388

104-
assert len(exact) == 2
105-
assert mock_users[1] in exact
106-
assert mock_users[2] in exact
107-
assert fuzzy == []
108-
assert unmatched == []
89+
assert len(matches) == 2
90+
assert any(u["id"] == 1 for u in matches)
91+
assert any(u["id"] == 2 for u in matches)
10992

11093
@patch("apps.github.management.commands.github_match_users.fuzz")
11194
def test_fuzzy_match(self, mock_fuzz, command, mock_users):
11295
"""Test fuzzy matching."""
113-
mock_fuzz.token_sort_ratio.side_effect = (
114-
lambda left, right: 90 if "peter" in right.lower() or "peter" in left.lower() else 10
115-
)
116-
96+
mock_fuzz.token_sort_ratio.side_effect = lambda _, s2: 90 if "peter" in s2.lower() else 10
11797
leaders_raw = ["pete_jones"]
118-
exact, fuzzy, unmatched = command.process_leaders(leaders_raw, 80, mock_users)
98+
matches = command._find_user_matches(leaders_raw, mock_users, 80)
11999

120-
assert exact == []
121-
assert len(fuzzy) == 1
122-
assert mock_users[3] in fuzzy
123-
assert unmatched == []
100+
assert len(matches) == 1
101+
assert matches[0]["id"] == 3
124102

125103
def test_unmatched_leader(self, command, mock_users):
126-
"""Test unmatched leader."""
104+
"""Test that an unknown leader returns no matches."""
127105
leaders_raw = ["unknown_leader"]
128-
exact, fuzzy, unmatched = command.process_leaders(leaders_raw, 100, mock_users)
129-
130-
assert exact == []
131-
assert fuzzy == []
132-
assert unmatched == ["unknown_leader"]
133-
134-
def test_mixed_matches(self, command, mock_users):
135-
"""Test a mix of exact, fuzzy, and unmatched leaders."""
136-
leaders_raw = ["john_doe", "pete_jones", "unknown_leader"]
137-
138-
with patch("apps.github.management.commands.github_match_users.fuzz") as mock_fuzz:
139-
140-
def ratio(s1, s2):
141-
return 85 if "peter" in s2.lower() and "pete" in s1.lower() else 50
142-
143-
mock_fuzz.token_sort_ratio.side_effect = ratio
144-
exact, fuzzy, unmatched = command.process_leaders(leaders_raw, 80, mock_users)
145-
146-
assert len(exact) == 1
147-
assert mock_users[1] in exact
148-
assert len(fuzzy) == 1
149-
assert mock_users[3] in fuzzy
150-
assert unmatched == ["unknown_leader"]
151-
152-
def test_duplicate_leaders(self, command, mock_users):
153-
"""Test with duplicate leaders in raw list."""
154-
leaders_raw = ["john_doe", "john_doe"]
155-
exact, fuzzy, unmatched = command.process_leaders(leaders_raw, 75, mock_users)
156-
157-
assert len(exact) == 1
158-
assert mock_users[1] in exact
159-
assert fuzzy == []
160-
assert unmatched == []
161-
162-
def test_empty_and_none_leaders(self, command, mock_users):
163-
"""Test with empty string and None in leaders raw list."""
164-
leaders_raw = ["", None, "john_doe"]
165-
exact, fuzzy, unmatched = command.process_leaders(leaders_raw, 75, mock_users)
166-
167-
assert len(exact) == 1
168-
assert mock_users[1] in exact
169-
assert fuzzy == []
170-
assert unmatched == []
171-
172-
def test_multiple_exact_matches_for_one_leader(self, command):
173-
"""Test when one leader name matches multiple users."""
174-
users = {
175-
1: {"id": 1, "login": "johndoe", "name": "Johnathan Doe"},
176-
2: {"id": 2, "login": "JohnDoe", "name": "John Doe"},
177-
}
178-
leaders_raw = ["JohnDoe"]
179-
exact, fuzzy, unmatched = command.process_leaders(leaders_raw, 75, users)
180-
181-
assert len(exact) == 2
182-
assert users[1] in exact
183-
assert users[2] in exact
184-
assert fuzzy == []
185-
assert unmatched == []
186-
187-
188-
@patch("apps.github.management.commands.github_match_users.User")
189-
@patch("apps.github.management.commands.github_match_users.Chapter")
190-
@patch("apps.github.management.commands.github_match_users.Committee")
191-
@patch("apps.github.management.commands.github_match_users.Project")
192-
@patch("apps.github.management.commands.github_match_users.Member")
193-
class TestHandleMethod:
194-
"""Test suite for the handle method of the command."""
195-
196-
@pytest.fixture
197-
def command(self):
198-
"""Return a command instance with mocked stdout."""
199-
command = Command()
200-
command.stdout = MagicMock()
201-
202-
return command
203-
204-
def test_invalid_model_name(
205-
self, mock_member, mock_project, mock_committee, mock_chapter, mock_user, command
206-
):
207-
"""Test handle with an invalid model name."""
208-
command.handle(model_name="invalid", threshold=75)
209-
command.stdout.write.assert_called_with(
210-
command.style.ERROR(
211-
"Invalid model name! Choose from: chapter, committee, project, member"
212-
)
213-
)
214-
215-
@pytest.mark.parametrize(
216-
("model_name", "model_class_str", "relation_field"),
217-
[
218-
("chapter", "Chapter", "suggested_leaders"),
219-
("committee", "Committee", "suggested_leaders"),
220-
("project", "Project", "suggested_leaders"),
221-
("member", "Member", "suggested_users"),
222-
],
223-
)
224-
def test_handle_with_valid_models(
225-
self,
226-
mock_member,
227-
mock_project,
228-
mock_committee,
229-
mock_chapter,
230-
mock_user,
231-
command,
232-
model_name,
233-
model_class_str,
234-
relation_field,
235-
):
236-
"""Test handle with different valid models."""
237-
mock_models = {
238-
"Chapter": mock_chapter,
239-
"Committee": mock_committee,
240-
"Project": mock_project,
241-
"Member": mock_member,
242-
}
243-
model_class = mock_models[model_class_str]
244-
245-
mock_user.objects.values.return_value = [
246-
{"id": 1, "login": "leader_one", "name": "Leader One"},
247-
{"id": 2, "login": "leader_two", "name": "Leader Two"},
248-
]
249-
250-
mock_instance = MagicMock()
251-
mock_instance.id = 1
252-
253-
if model_name == "member":
254-
mock_instance.username = "leader_one"
255-
mock_instance.real_name = "Leader Two"
256-
else:
257-
mock_instance.leaders_raw = ["leader_one", "leader_two"]
258-
259-
model_class.objects.prefetch_related.return_value = [mock_instance]
260-
261-
command.handle(model_name=model_name, threshold=90)
262-
263-
model_class.objects.prefetch_related.assert_called_once_with(relation_field)
264-
265-
relation = getattr(mock_instance, relation_field)
266-
relation.set.assert_called_once_with({1, 2})
267-
268-
command.stdout.write.assert_any_call(f"Processing {model_name} 1...")
269-
command.stdout.write.assert_any_call("Exact match found for leader_one: leader_one")
270-
271-
def test_handle_with_no_users(
272-
self, mock_member, mock_project, mock_committee, mock_chapter, mock_user, command
273-
):
274-
"""Test handle when there are no users in the database."""
275-
mock_user.objects.values.return_value = []
276-
mock_chapter_instance = MagicMock(id=1, leaders_raw=["some_leader"])
277-
mock_chapter.objects.prefetch_related.return_value = [mock_chapter_instance]
278-
279-
command.handle(model_name="chapter", threshold=75)
280-
281-
command.stdout.write.assert_any_call("Processing chapter 1...")
282-
283-
unmatched_call = [
284-
c for c in command.stdout.write.call_args_list if "Unmatched" in c.args[0]
285-
]
286-
287-
assert len(unmatched_call) == 1
288-
assert "['some_leader']" in unmatched_call[0].args[0]
289-
290-
mock_chapter_instance.suggested_leaders.set.assert_called_once_with(set())
291-
292-
def test_handle_with_no_leaders_in_instance(
293-
self, mock_member, mock_project, mock_committee, mock_chapter, mock_user, command
294-
):
295-
"""Test handle when an instance has no leaders."""
296-
mock_user.objects.values.return_value = [
297-
{"id": 1, "login": "user1", "name": "User One"},
298-
]
299-
mock_chapter_instance = MagicMock(id=1, leaders_raw=[])
300-
mock_chapter.objects.prefetch_related.return_value = [mock_chapter_instance]
301-
302-
command.handle(model_name="chapter", threshold=75)
303-
304-
command.stdout.write.assert_any_call("Processing chapter 1...")
305-
306-
unmatched_call = [
307-
c for c in command.stdout.write.call_args_list if "Unmatched" in c.args[0]
308-
]
309-
310-
assert len(unmatched_call) == 0
311-
312-
mock_chapter_instance.suggested_leaders.set.assert_called_once_with(set())
106+
matches = command._find_user_matches(leaders_raw, mock_users, 100)
107+
assert matches == []

0 commit comments

Comments
 (0)