Back to Repositories

Testing Redis Rate Limiting Implementation in Conductor OSS

This test suite validates the Redis-based rate limiting functionality in Conductor OSS, focusing on task execution frequency control and limit enforcement. The tests ensure proper rate limit checking behavior for different task definition configurations.

Test Coverage Overview

The test suite provides comprehensive coverage of rate limiting scenarios in Redis persistence.

Key areas tested include:
  • Default behavior with no rate limits configured
  • Rate limit compliance within configured thresholds
  • Rate limit enforcement when exceeding defined limits
  • Integration with TaskDef and TaskModel components

Implementation Analysis

The testing approach utilizes JUnit with Spring Runner for dependency injection and context management. Tests employ a mock Redis implementation (JedisMock) to simulate database interactions.

Notable patterns include:
  • Before-test initialization with mocked dependencies
  • Isolated test cases for distinct rate limiting scenarios
  • UUID generation for unique task identification

Technical Details

Testing infrastructure includes:
  • JUnit 4 test framework
  • Spring Test Context framework
  • Mock Redis implementation via JedisMock
  • ObjectMapper for JSON serialization
  • Mockito for dependency mocking

Best Practices Demonstrated

The test suite exemplifies several testing best practices:

  • Clear test method naming reflecting test scenarios
  • Proper test isolation and setup
  • Effective use of assertion methods
  • Comprehensive edge case coverage
  • Clean separation of test configurations

conductor-oss/conductor

redis-persistence/src/test/java/com/netflix/conductor/redis/dao/RedisRateLimitDAOTest.java

            
/*
 * Copyright 2021 Conductor Authors.
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */
package com.netflix.conductor.redis.dao;

import java.util.UUID;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;

import com.netflix.conductor.common.config.TestObjectMapperConfiguration;
import com.netflix.conductor.common.metadata.tasks.TaskDef;
import com.netflix.conductor.core.config.ConductorProperties;
import com.netflix.conductor.model.TaskModel;
import com.netflix.conductor.redis.config.RedisProperties;
import com.netflix.conductor.redis.jedis.JedisMock;
import com.netflix.conductor.redis.jedis.JedisProxy;

import com.fasterxml.jackson.databind.ObjectMapper;
import redis.clients.jedis.commands.JedisCommands;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;

@ContextConfiguration(classes = {TestObjectMapperConfiguration.class})
@RunWith(SpringRunner.class)
public class RedisRateLimitDAOTest {

    private RedisRateLimitingDAO rateLimitingDao;

    @Autowired private ObjectMapper objectMapper;

    @Before
    public void init() {
        ConductorProperties conductorProperties = mock(ConductorProperties.class);
        RedisProperties properties = mock(RedisProperties.class);
        JedisCommands jedisMock = new JedisMock();
        JedisProxy jedisProxy = new JedisProxy(jedisMock);

        rateLimitingDao =
                new RedisRateLimitingDAO(jedisProxy, objectMapper, conductorProperties, properties);
    }

    @Test
    public void testExceedsRateLimitWhenNoRateLimitSet() {
        TaskDef taskDef = new TaskDef("TestTaskDefinition");
        TaskModel task = new TaskModel();
        task.setTaskId(UUID.randomUUID().toString());
        task.setTaskDefName(taskDef.getName());
        assertFalse(rateLimitingDao.exceedsRateLimitPerFrequency(task, taskDef));
    }

    @Test
    public void testExceedsRateLimitWithinLimit() {
        TaskDef taskDef = new TaskDef("TestTaskDefinition");
        taskDef.setRateLimitFrequencyInSeconds(60);
        taskDef.setRateLimitPerFrequency(20);
        TaskModel task = new TaskModel();
        task.setTaskId(UUID.randomUUID().toString());
        task.setTaskDefName(taskDef.getName());
        assertFalse(rateLimitingDao.exceedsRateLimitPerFrequency(task, taskDef));
    }

    @Test
    public void testExceedsRateLimitOutOfLimit() {
        TaskDef taskDef = new TaskDef("TestTaskDefinition");
        taskDef.setRateLimitFrequencyInSeconds(60);
        taskDef.setRateLimitPerFrequency(1);
        TaskModel task = new TaskModel();
        task.setTaskId(UUID.randomUUID().toString());
        task.setTaskDefName(taskDef.getName());
        assertFalse(rateLimitingDao.exceedsRateLimitPerFrequency(task, taskDef));
        assertTrue(rateLimitingDao.exceedsRateLimitPerFrequency(task, taskDef));
    }
}