|
1 | 1 | import pytest |
2 | 2 | import uuid |
3 | | -from datetime import timedelta |
| 3 | +from datetime import timedelta, datetime, timezone |
4 | 4 | from unittest.mock import AsyncMock, Mock, PropertyMock |
5 | 5 |
|
6 | 6 | from cadence.api.v1.common_pb2 import WorkflowExecution |
@@ -323,6 +323,76 @@ def test_zero_delay_start_is_valid(self): |
323 | 323 | validated = _validate_and_apply_defaults(options) |
324 | 324 | assert validated["delay_start"] == timedelta(0) |
325 | 325 |
|
| 326 | + @pytest.mark.asyncio |
| 327 | + async def test_build_request_with_first_run_at_timezone_aware(self, mock_client): |
| 328 | + """Test building request with timezone-aware first_run_at.""" |
| 329 | + client = Client(domain="test-domain", target="localhost:7933") |
| 330 | + |
| 331 | + first_run = datetime(2024, 6, 1, 12, 0, 0, tzinfo=timezone.utc) |
| 332 | + |
| 333 | + options = StartWorkflowOptions( |
| 334 | + task_list="test-task-list", |
| 335 | + execution_start_to_close_timeout=timedelta(minutes=10), |
| 336 | + task_start_to_close_timeout=timedelta(seconds=30), |
| 337 | + cron_schedule="0 * * * *", |
| 338 | + first_run_at=first_run, |
| 339 | + ) |
| 340 | + |
| 341 | + request = client._build_start_workflow_request("TestWorkflow", (), options) |
| 342 | + |
| 343 | + assert request.HasField("first_run_at") |
| 344 | + # Convert back and verify |
| 345 | + result_dt = request.first_run_at.ToDatetime() |
| 346 | + assert result_dt == first_run.replace( |
| 347 | + tzinfo=None |
| 348 | + ) # ToDatetime returns naive UTC |
| 349 | + |
| 350 | + def test_first_run_at_naive_datetime_raises_error(self): |
| 351 | + """Test that timezone-naive first_run_at raises ValueError.""" |
| 352 | + options = StartWorkflowOptions( |
| 353 | + task_list="test-task-list", |
| 354 | + execution_start_to_close_timeout=timedelta(minutes=10), |
| 355 | + first_run_at=datetime(2024, 1, 1, 12, 0, 0), # Naive |
| 356 | + ) |
| 357 | + with pytest.raises(ValueError, match="must be timezone-aware"): |
| 358 | + _validate_and_apply_defaults(options) |
| 359 | + |
| 360 | + def test_first_run_at_aware_datetime_is_valid(self): |
| 361 | + """Test that timezone-aware first_run_at is valid.""" |
| 362 | + options = StartWorkflowOptions( |
| 363 | + task_list="test-task-list", |
| 364 | + execution_start_to_close_timeout=timedelta(minutes=10), |
| 365 | + first_run_at=datetime(2024, 1, 1, 12, 0, 0, tzinfo=timezone.utc), |
| 366 | + ) |
| 367 | + # Should not raise |
| 368 | + validated = _validate_and_apply_defaults(options) |
| 369 | + assert validated["first_run_at"] == datetime( |
| 370 | + 2024, 1, 1, 12, 0, 0, tzinfo=timezone.utc |
| 371 | + ) |
| 372 | + |
| 373 | + def test_first_run_at_before_epoch_raises_error(self): |
| 374 | + """Test that first_run_at before Unix epoch raises ValueError.""" |
| 375 | + options = StartWorkflowOptions( |
| 376 | + task_list="test-task-list", |
| 377 | + execution_start_to_close_timeout=timedelta(minutes=10), |
| 378 | + first_run_at=datetime(1960, 1, 1, 0, 0, 0, tzinfo=timezone.utc), |
| 379 | + ) |
| 380 | + with pytest.raises(ValueError, match="cannot be before Unix epoch"): |
| 381 | + _validate_and_apply_defaults(options) |
| 382 | + |
| 383 | + def test_first_run_at_at_epoch_is_valid(self): |
| 384 | + """Test that first_run_at exactly at Unix epoch is valid.""" |
| 385 | + options = StartWorkflowOptions( |
| 386 | + task_list="test-task-list", |
| 387 | + execution_start_to_close_timeout=timedelta(minutes=10), |
| 388 | + first_run_at=datetime(1970, 1, 1, 0, 0, 0, tzinfo=timezone.utc), |
| 389 | + ) |
| 390 | + # Should not raise |
| 391 | + validated = _validate_and_apply_defaults(options) |
| 392 | + assert validated["first_run_at"] == datetime( |
| 393 | + 1970, 1, 1, 0, 0, 0, tzinfo=timezone.utc |
| 394 | + ) |
| 395 | + |
326 | 396 |
|
327 | 397 | class TestClientStartWorkflow: |
328 | 398 | """Test Client.start_workflow method.""" |
|
0 commit comments