|
| 1 | +from datetime import date, datetime |
| 2 | + |
| 3 | +import pytest |
| 4 | +from freezegun import freeze_time |
| 5 | + |
| 6 | +from pgcommitfest.commitfest.models import ( |
| 7 | + CommitFest, |
| 8 | + Patch, |
| 9 | + PatchOnCommitFest, |
| 10 | + Topic, |
| 11 | +) |
| 12 | +from pgcommitfest.userprofile.models import UserProfile |
| 13 | + |
| 14 | + |
| 15 | +@pytest.fixture |
| 16 | +def topic(db): |
| 17 | + return Topic.objects.create(topic="General") |
| 18 | + |
| 19 | + |
| 20 | +@pytest.fixture |
| 21 | +def alice(db, django_user_model): |
| 22 | + user = django_user_model.objects.create_user( |
| 23 | + username="alice", |
| 24 | + email="alice@example.com", |
| 25 | + first_name="Alice", |
| 26 | + last_name="Smith", |
| 27 | + ) |
| 28 | + UserProfile.objects.create(user=user, notify_all_author=True) |
| 29 | + return user |
| 30 | + |
| 31 | + |
| 32 | +def create_closed_cf(name, startdate, enddate): |
| 33 | + """Helper to create a closed CF for padding.""" |
| 34 | + return CommitFest.objects.create( |
| 35 | + name=name, |
| 36 | + status=CommitFest.STATUS_CLOSED, |
| 37 | + startdate=startdate, |
| 38 | + enddate=enddate, |
| 39 | + ) |
| 40 | + |
| 41 | + |
| 42 | +@pytest.mark.django_db |
| 43 | +@freeze_time("2024-12-05") |
| 44 | +def test_inprogress_cf_closes_when_enddate_passed(topic, alice): |
| 45 | + """When an in_progress CF's enddate has passed, it should be closed.""" |
| 46 | + # Create some closed CFs for padding (relevant_commitfests needs history) |
| 47 | + create_closed_cf("2024-07", date(2024, 7, 1), date(2024, 7, 31)) |
| 48 | + create_closed_cf("2024-09", date(2024, 9, 1), date(2024, 9, 30)) |
| 49 | + |
| 50 | + # Create an in_progress CF that ended |
| 51 | + in_progress_cf = CommitFest.objects.create( |
| 52 | + name="2024-11", |
| 53 | + status=CommitFest.STATUS_INPROGRESS, |
| 54 | + startdate=date(2024, 11, 1), |
| 55 | + enddate=date(2024, 11, 30), |
| 56 | + ) |
| 57 | + # Create the next open CF (required for auto_move) |
| 58 | + open_cf = CommitFest.objects.create( |
| 59 | + name="2025-01", |
| 60 | + status=CommitFest.STATUS_OPEN, |
| 61 | + startdate=date(2025, 1, 1), |
| 62 | + enddate=date(2025, 1, 31), |
| 63 | + ) |
| 64 | + # Create draft CF |
| 65 | + CommitFest.objects.create( |
| 66 | + name="2025-draft", |
| 67 | + status=CommitFest.STATUS_OPEN, |
| 68 | + startdate=date(2024, 7, 1), |
| 69 | + enddate=date(2025, 3, 31), |
| 70 | + draft=True, |
| 71 | + ) |
| 72 | + |
| 73 | + # Create a patch with recent activity that should be auto-moved |
| 74 | + patch = Patch.objects.create( |
| 75 | + name="Test Patch", |
| 76 | + topic=topic, |
| 77 | + lastmail=datetime(2024, 11, 25), |
| 78 | + ) |
| 79 | + patch.authors.add(alice) |
| 80 | + PatchOnCommitFest.objects.create( |
| 81 | + patch=patch, |
| 82 | + commitfest=in_progress_cf, |
| 83 | + enterdate=datetime.now(), |
| 84 | + status=PatchOnCommitFest.STATUS_REVIEW, |
| 85 | + ) |
| 86 | + |
| 87 | + CommitFest._refresh_relevant_commitfests(for_update=False) |
| 88 | + |
| 89 | + in_progress_cf.refresh_from_db() |
| 90 | + assert in_progress_cf.status == CommitFest.STATUS_CLOSED |
| 91 | + |
| 92 | + # Patch should have been moved to the open CF |
| 93 | + patch.refresh_from_db() |
| 94 | + assert patch.current_commitfest().id == open_cf.id |
| 95 | + |
| 96 | + |
| 97 | +@pytest.mark.django_db |
| 98 | +@freeze_time("2025-01-15") |
| 99 | +def test_open_cf_becomes_inprogress_when_startdate_reached(): |
| 100 | + """When an open CF's startdate is reached, it becomes in_progress.""" |
| 101 | + # Create some closed CFs for padding |
| 102 | + create_closed_cf("2024-07", date(2024, 7, 1), date(2024, 7, 31)) |
| 103 | + create_closed_cf("2024-09", date(2024, 9, 1), date(2024, 9, 30)) |
| 104 | + create_closed_cf("2024-11", date(2024, 11, 1), date(2024, 11, 30)) |
| 105 | + |
| 106 | + open_cf = CommitFest.objects.create( |
| 107 | + name="2025-01", |
| 108 | + status=CommitFest.STATUS_OPEN, |
| 109 | + startdate=date(2025, 1, 1), |
| 110 | + enddate=date(2025, 1, 31), |
| 111 | + ) |
| 112 | + # Create draft CF |
| 113 | + CommitFest.objects.create( |
| 114 | + name="2025-draft", |
| 115 | + status=CommitFest.STATUS_OPEN, |
| 116 | + startdate=date(2024, 7, 1), |
| 117 | + enddate=date(2025, 3, 31), |
| 118 | + draft=True, |
| 119 | + ) |
| 120 | + |
| 121 | + CommitFest._refresh_relevant_commitfests(for_update=False) |
| 122 | + |
| 123 | + open_cf.refresh_from_db() |
| 124 | + assert open_cf.status == CommitFest.STATUS_INPROGRESS |
| 125 | + |
| 126 | + # A new open CF should have been created |
| 127 | + new_open = CommitFest.objects.filter( |
| 128 | + status=CommitFest.STATUS_OPEN, draft=False |
| 129 | + ).first() |
| 130 | + assert new_open is not None |
| 131 | + assert new_open.startdate > open_cf.enddate |
| 132 | + |
| 133 | + |
| 134 | +@pytest.mark.django_db |
| 135 | +@freeze_time("2025-02-05") |
| 136 | +def test_open_cf_closes_when_enddate_passed(topic, alice): |
| 137 | + """When an open CF's enddate has passed (skipping in_progress), it closes.""" |
| 138 | + # Create some closed CFs for padding |
| 139 | + create_closed_cf("2024-07", date(2024, 7, 1), date(2024, 7, 31)) |
| 140 | + create_closed_cf("2024-09", date(2024, 9, 1), date(2024, 9, 30)) |
| 141 | + create_closed_cf("2024-11", date(2024, 11, 1), date(2024, 11, 30)) |
| 142 | + |
| 143 | + open_cf = CommitFest.objects.create( |
| 144 | + name="2025-01", |
| 145 | + status=CommitFest.STATUS_OPEN, |
| 146 | + startdate=date(2025, 1, 1), |
| 147 | + enddate=date(2025, 1, 31), |
| 148 | + ) |
| 149 | + # Create draft CF |
| 150 | + CommitFest.objects.create( |
| 151 | + name="2025-draft", |
| 152 | + status=CommitFest.STATUS_OPEN, |
| 153 | + startdate=date(2024, 7, 1), |
| 154 | + enddate=date(2025, 3, 31), |
| 155 | + draft=True, |
| 156 | + ) |
| 157 | + |
| 158 | + # Create a patch with recent activity |
| 159 | + patch = Patch.objects.create( |
| 160 | + name="Test Patch", |
| 161 | + topic=topic, |
| 162 | + lastmail=datetime(2025, 1, 25), |
| 163 | + ) |
| 164 | + patch.authors.add(alice) |
| 165 | + PatchOnCommitFest.objects.create( |
| 166 | + patch=patch, |
| 167 | + commitfest=open_cf, |
| 168 | + enterdate=datetime.now(), |
| 169 | + status=PatchOnCommitFest.STATUS_REVIEW, |
| 170 | + ) |
| 171 | + |
| 172 | + CommitFest._refresh_relevant_commitfests(for_update=False) |
| 173 | + |
| 174 | + open_cf.refresh_from_db() |
| 175 | + assert open_cf.status == CommitFest.STATUS_CLOSED |
| 176 | + |
| 177 | + # A new open CF should have been created |
| 178 | + new_open = CommitFest.objects.filter( |
| 179 | + status=CommitFest.STATUS_OPEN, draft=False |
| 180 | + ).first() |
| 181 | + assert new_open is not None |
| 182 | + |
| 183 | + # Patch should have been moved to the new open CF |
| 184 | + patch.refresh_from_db() |
| 185 | + assert patch.current_commitfest().id == new_open.id |
| 186 | + |
| 187 | + |
| 188 | +@pytest.mark.django_db |
| 189 | +@freeze_time("2025-01-15") |
| 190 | +def test_draft_cf_created_when_missing(): |
| 191 | + """When no draft CF exists, one should be created.""" |
| 192 | + # Create closed CFs for padding |
| 193 | + create_closed_cf("2024-07", date(2024, 7, 1), date(2024, 7, 31)) |
| 194 | + create_closed_cf("2024-09", date(2024, 9, 1), date(2024, 9, 30)) |
| 195 | + create_closed_cf("2024-11", date(2024, 11, 1), date(2024, 11, 30)) |
| 196 | + |
| 197 | + # Create only regular CFs |
| 198 | + CommitFest.objects.create( |
| 199 | + name="2025-01", |
| 200 | + status=CommitFest.STATUS_OPEN, |
| 201 | + startdate=date(2025, 3, 1), |
| 202 | + enddate=date(2025, 3, 31), |
| 203 | + ) |
| 204 | + |
| 205 | + assert not CommitFest.objects.filter(draft=True).exists() |
| 206 | + |
| 207 | + CommitFest._refresh_relevant_commitfests(for_update=False) |
| 208 | + |
| 209 | + # A draft CF should have been created |
| 210 | + draft_cf = CommitFest.objects.filter(draft=True).first() |
| 211 | + assert draft_cf is not None |
| 212 | + assert draft_cf.status == CommitFest.STATUS_OPEN |
| 213 | + |
| 214 | + |
| 215 | +@pytest.mark.django_db |
| 216 | +@freeze_time("2025-04-05") |
| 217 | +def test_draft_cf_closes_when_enddate_passed(topic, alice): |
| 218 | + """When a draft CF's enddate has passed, it should be closed.""" |
| 219 | + # Create closed CFs for padding |
| 220 | + create_closed_cf("2024-07", date(2024, 7, 1), date(2024, 7, 31)) |
| 221 | + create_closed_cf("2024-09", date(2024, 9, 1), date(2024, 9, 30)) |
| 222 | + create_closed_cf("2024-11", date(2024, 11, 1), date(2024, 11, 30)) |
| 223 | + |
| 224 | + # Create an open regular CF (required) |
| 225 | + CommitFest.objects.create( |
| 226 | + name="2025-03", |
| 227 | + status=CommitFest.STATUS_OPEN, |
| 228 | + startdate=date(2025, 5, 1), |
| 229 | + enddate=date(2025, 5, 31), |
| 230 | + ) |
| 231 | + |
| 232 | + # Create a draft CF that ended |
| 233 | + draft_cf = CommitFest.objects.create( |
| 234 | + name="2025-draft", |
| 235 | + status=CommitFest.STATUS_OPEN, |
| 236 | + startdate=date(2025, 1, 1), |
| 237 | + enddate=date(2025, 3, 31), |
| 238 | + draft=True, |
| 239 | + ) |
| 240 | + |
| 241 | + # Create a patch with recent activity |
| 242 | + patch = Patch.objects.create( |
| 243 | + name="Draft Patch", |
| 244 | + topic=topic, |
| 245 | + lastmail=datetime(2025, 3, 25), |
| 246 | + ) |
| 247 | + patch.authors.add(alice) |
| 248 | + PatchOnCommitFest.objects.create( |
| 249 | + patch=patch, |
| 250 | + commitfest=draft_cf, |
| 251 | + enterdate=datetime.now(), |
| 252 | + status=PatchOnCommitFest.STATUS_REVIEW, |
| 253 | + ) |
| 254 | + |
| 255 | + CommitFest._refresh_relevant_commitfests(for_update=False) |
| 256 | + |
| 257 | + draft_cf.refresh_from_db() |
| 258 | + assert draft_cf.status == CommitFest.STATUS_CLOSED |
| 259 | + |
| 260 | + # A new draft CF should have been created |
| 261 | + new_draft = CommitFest.objects.filter( |
| 262 | + draft=True, status=CommitFest.STATUS_OPEN |
| 263 | + ).first() |
| 264 | + assert new_draft is not None |
| 265 | + assert new_draft.startdate > draft_cf.enddate |
| 266 | + |
| 267 | + # Patch should have been moved to the new draft CF |
| 268 | + patch.refresh_from_db() |
| 269 | + assert patch.current_commitfest().id == new_draft.id |
| 270 | + |
| 271 | + |
| 272 | +@pytest.mark.django_db |
| 273 | +@freeze_time("2025-01-15") |
| 274 | +def test_no_changes_when_up_to_date(): |
| 275 | + """When commitfests are up to date, no changes should be made.""" |
| 276 | + # Create closed CFs for padding |
| 277 | + create_closed_cf("2024-07", date(2024, 7, 1), date(2024, 7, 31)) |
| 278 | + create_closed_cf("2024-09", date(2024, 9, 1), date(2024, 9, 30)) |
| 279 | + |
| 280 | + # Create CFs that are all up to date |
| 281 | + in_progress_cf = CommitFest.objects.create( |
| 282 | + name="2025-01", |
| 283 | + status=CommitFest.STATUS_INPROGRESS, |
| 284 | + startdate=date(2025, 1, 1), |
| 285 | + enddate=date(2025, 1, 31), |
| 286 | + ) |
| 287 | + open_cf = CommitFest.objects.create( |
| 288 | + name="2025-03", |
| 289 | + status=CommitFest.STATUS_OPEN, |
| 290 | + startdate=date(2025, 3, 1), |
| 291 | + enddate=date(2025, 3, 31), |
| 292 | + ) |
| 293 | + draft_cf = CommitFest.objects.create( |
| 294 | + name="2025-draft", |
| 295 | + status=CommitFest.STATUS_OPEN, |
| 296 | + startdate=date(2025, 1, 1), |
| 297 | + enddate=date(2025, 3, 31), |
| 298 | + draft=True, |
| 299 | + ) |
| 300 | + |
| 301 | + initial_cf_count = CommitFest.objects.count() |
| 302 | + |
| 303 | + CommitFest._refresh_relevant_commitfests(for_update=False) |
| 304 | + |
| 305 | + # All statuses should remain unchanged |
| 306 | + in_progress_cf.refresh_from_db() |
| 307 | + open_cf.refresh_from_db() |
| 308 | + draft_cf.refresh_from_db() |
| 309 | + |
| 310 | + assert in_progress_cf.status == CommitFest.STATUS_INPROGRESS |
| 311 | + assert open_cf.status == CommitFest.STATUS_OPEN |
| 312 | + assert draft_cf.status == CommitFest.STATUS_OPEN |
| 313 | + |
| 314 | + # No new CFs should have been created |
| 315 | + assert CommitFest.objects.count() == initial_cf_count |
0 commit comments