branhoff / hooter-the-tutor

0 stars 2 forks source link

Add unit test edge cases for streaks module #16

Open branhoff opened 4 months ago

branhoff commented 4 months ago

We need to improve our test coverage for the Streaks module by adding unit tests for various edge cases.

Tasks:

  1. Add a test for initializing a new user's streak data
  2. Add a test for updating a streak after exactly 24 hours
  3. Add a test for handling streak resets after more than 48 hours
  4. Add a test for very long streaks (e.g., 1000+ days)
  5. Add a test for handling invalid date formats in stored data
  6. Add a test for concurrent updates to the same user's streak

For each test, follow the established pattern:

Example structure for each test:

@pytest.mark.asyncio
async def test_streak_[scenario_name](cog):
    # Arrange
    member = AsyncMock(spec=Member)
    member.id = "12345"
    member.name = "TestUser"

    mock_streaks_data = {
        # Appropriate mock data for the test scenario
    }

    with patch.object(cog, 'load_streaks', return_value=mock_streaks_data), \
         patch.object(cog, 'save_streaks') as mock_save:

        # Act
        # Call the relevant method with appropriate parameters

        # Assert
        # Verify the expected behavior
        # Check if save_streaks was called with the expected updated data

Definition of Done:

branhoff commented 3 months ago

I haven't added edge cases exactly, but I did add a basic set of tests that test basic incrementing and resetting of streaks. These service as the basis for future more complicated tests.

@pytest.mark.asyncio
async def test_increment_streak_next_day(cog):
  # Arrange
  user_id = "12345"
  username = "TestUser"

  # Mock initial streak data
  initial_streak_data = {
    user_id: {
      "username": username,
      "current_streak": 1,
      "longest_streak": 1,
      "last_join_date": (datetime.now() - timedelta(days=1)).date(),
      "join_time": None
    }
  }

  # Mock member and channel
  member = AsyncMock(spec=Member)
  member.id = user_id
  member.name = username
  channel = AsyncMock()

  with patch.object(cog, 'load_streaks', return_value=initial_streak_data), \
      patch.object(cog, 'save_streaks') as mock_save, \
      patch('cogs.streaks.datetime') as mock_datetime:
    # Set join time to next day
    join_time = datetime.now()
    mock_datetime.now.return_value = join_time

    # Simulate joining
    cog.handle_join(user_id, username, join_time)

    # Set leave time to 30 minutes after join time
    leave_time = join_time + timedelta(minutes=30)
    mock_datetime.now.return_value = leave_time

    # Act: Simulate leaving
    await cog.handle_leave(user_id, username, core.MINIMUM_MINUTES, member,
                           channel, leave_time)

    # Assert
    mock_save.assert_called()
    saved_data = mock_save.call_args[0][0]
    assert saved_data[user_id]["current_streak"] == 2
    assert saved_data[user_id]["longest_streak"] == 2
    assert saved_data[user_id]["last_join_date"] == leave_time.date()
    assert saved_data[user_id]["join_time"] is None

@pytest.mark.asyncio
async def test_reset_streak_after_two_days(cog):
  # Arrange
  user_id = "12345"
  username = "TestUser"

  # Mock initial streak data
  initial_streak_data = {
    user_id: {
      "username": username,
      "current_streak": 5,
      "longest_streak": 10,
      "last_join_date": (datetime.now() - timedelta(days=2)).date(),
      "join_time": None
    }
  }

  # Mock member and channel
  member = AsyncMock(spec=Member)
  member.id = user_id
  member.name = username
  channel = AsyncMock()

  with patch.object(cog, 'load_streaks', return_value=initial_streak_data), \
      patch.object(cog, 'save_streaks') as mock_save, \
      patch('cogs.streaks.datetime') as mock_datetime:
    # Set join time to 2 days later
    join_time = datetime.now() + timedelta(days=2)
    mock_datetime.now.return_value = join_time

    # Simulate joining
    cog.handle_join(user_id, username, join_time)

    # Set leave time to 30 minutes after join time
    leave_time = join_time + timedelta(minutes=30)
    mock_datetime.now.return_value = leave_time

    # Act: Simulate leaving
    await cog.handle_leave(user_id, username, core.MINIMUM_MINUTES, member,
                           channel, leave_time)

    # Assert
    mock_save.assert_called()
    saved_data = mock_save.call_args[0][0]
    assert saved_data[user_id]["current_streak"] == 1
    assert saved_data[user_id]["longest_streak"] == 10
    assert saved_data[user_id]["last_join_date"] == leave_time.date()
    assert saved_data[user_id]["join_time"] is None