MaintenanceEventIdentityTest.java

package redis.clients.jedis;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;

import java.util.Collections;
import java.util.List;

import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;

/**
 * Identity semantics of {@link MaintenanceEvent#identity()}: seq for all types (including the
 * cluster SMIGRATING/SMIGRATED family); MOVING is also keyed by the original target endpoint, so
 * concurrent MOVINGs to different endpoints stay distinct operations. Identity excludes payload
 * that may vary between deliveries of the same operation (remaining time, shard diagnostics, slot
 * ranges).
 */
@Tag("sch")
public class MaintenanceEventIdentityTest {

  private static final HostAndPort TARGET_B = new HostAndPort("node-b.example.com", 6380);
  private static final HostAndPort TARGET_C = new HostAndPort("node-c.example.com", 6381);

  @Test
  public void seqIsTheIdentityForNonMovingEvents() {
    assertEquals(new FailingOverEvent(5L, 10, "1").identity(),
      new FailingOverEvent(5L, 99, "2").identity(), "same seq: same operation");
    assertEquals(new FailingOverEvent(5L, 10, "1").identity().hashCode(),
      new FailingOverEvent(5L, 99, "2").identity().hashCode());

    assertNotEquals(new FailingOverEvent(5L, 10, "1").identity(),
      new FailingOverEvent(6L, 10, "1").identity());
  }

  @Test
  public void movingIdentity_includesTargetExcludesTtl() {
    assertEquals(new MovingEvent(5L, 10, TARGET_B).identity(),
      new MovingEvent(5L, 99, TARGET_B).identity(),
      "re-delivery with adjusted remaining time: same operation");
    assertEquals(new MovingEvent(5L, 10, TARGET_B).identity().hashCode(),
      new MovingEvent(5L, 99, TARGET_B).identity().hashCode());

    assertNotEquals(new MovingEvent(5L, 10, TARGET_B).identity(),
      new MovingEvent(5L, 10, TARGET_C).identity(),
      "seq is per-source: same seq to another target is a distinct operation");
    assertNotEquals(new MovingEvent(5L, 10, TARGET_B).identity(),
      new MovingEvent(5L, 10, null).identity(), "'none' is a distinct identity");
    assertEquals(new MovingEvent(5L, 10, null).identity(), new MovingEvent(5L, 20, null).identity(),
      "'none' identities merge by seq");
  }

  @Test
  public void movingIdentity_neverEqualsSeqOnlyIdentity() {
    // A MOVING identity is a composite value; it can never equal a plain seq, in either direction.
    assertNotEquals(new MovingEvent(5L, 10, null).identity(),
      new FailingOverEvent(5L, 10, "1").identity());
    assertNotEquals(new FailingOverEvent(5L, 10, "1").identity(),
      new MovingEvent(5L, 10, null).identity());
  }

  @Test
  public void clusterEventsUseSeqIdentity() {
    HashSlotRanges slotsA = HashSlotRanges.parse("0-100");
    HashSlotRanges slotsB = HashSlotRanges.parse("200");
    List<SlotMigration> delta = Collections
        .singletonList(new SlotMigration(TARGET_B, TARGET_C, slotsA));

    assertEquals(new SMigratingEvent(5L, slotsA).identity(),
      new SMigratingEvent(5L, slotsB).identity(), "same seq: same operation regardless of slots");
    assertEquals(new SMigratingEvent(5L, slotsA).identity().hashCode(),
      new SMigratingEvent(5L, slotsB).identity().hashCode());
    assertNotEquals(new SMigratingEvent(5L, slotsA).identity(),
      new SMigratingEvent(6L, slotsA).identity());

    assertEquals(new SMigratedEvent(5L, delta).identity(),
      new SMigratedEvent(5L, Collections.emptyList()).identity(),
      "same seq: same operation regardless of the slot delta");
    assertNotEquals(new SMigratedEvent(5L, delta).identity(),
      new SMigratedEvent(6L, delta).identity());
  }
}