@Test
  public void delete_characteristic() {
    DbSession batchSession = mock(DbSession.class);
    when(dbClient.openSession(true)).thenReturn(batchSession);

    when(ruleDao.selectRulesByDebtSubCharacteristicId(batchSession, subCharacteristicDto.getId()))
        .thenReturn(
            newArrayList(
                new RuleDto()
                    .setSubCharacteristicId(subCharacteristicDto.getId())
                    .setRemediationFunction(DebtRemediationFunction.Type.LINEAR_OFFSET.toString())
                    .setRemediationCoefficient("2h")
                    .setRemediationOffset("5min")));
    when(dao.selectCharacteristicsByParentId(1, batchSession))
        .thenReturn(newArrayList(subCharacteristicDto));
    when(dao.selectById(batchSession, 1)).thenReturn(characteristicDto);

    service.delete(1);

    verify(ruleDao).update(eq(batchSession), ruleCaptor.capture());

    verify(dao, times(2)).update(characteristicCaptor.capture(), eq(batchSession));
    CharacteristicDto subCharacteristicDto = characteristicCaptor.getAllValues().get(0);
    CharacteristicDto characteristicDto = characteristicCaptor.getAllValues().get(1);

    // Sub characteristic is disable
    assertThat(subCharacteristicDto.getId()).isEqualTo(2);
    assertThat(subCharacteristicDto.isEnabled()).isFalse();
    assertThat(subCharacteristicDto.getUpdatedAt()).isEqualTo(now);

    // Characteristic is disable
    assertThat(characteristicDto.getId()).isEqualTo(1);
    assertThat(characteristicDto.isEnabled()).isFalse();
    assertThat(characteristicDto.getUpdatedAt()).isEqualTo(now);
  }
  @Test
  public void fail_to_create_sub_characteristic_when_name_already_used() {
    when(dao.selectByName(session, "Compilation")).thenReturn(new CharacteristicDto());
    when(dao.selectById(session, 1)).thenReturn(characteristicDto);

    try {
      service.create("Compilation", 1);
      fail();
    } catch (BadRequestException e) {
      assertThat(e.firstError().getKey()).isEqualTo("errors.is_already_used");
      assertThat(e.firstError().getParams()[0]).isEqualTo("Compilation");
    }
  }
  @Test
  public void do_nothing_when_move_down_and_already_on_bottom() {
    when(dao.selectById(session, 10))
        .thenReturn(new CharacteristicDto().setId(10).setKey("MEMORY_EFFICIENCY").setOrder(2));
    when(dao.selectEnabledRootCharacteristics(session))
        .thenReturn(
            newArrayList(
                new CharacteristicDto().setId(2).setKey("PORTABILITY").setOrder(1),
                new CharacteristicDto().setId(10).setKey("MEMORY_EFFICIENCY").setOrder(2)));

    service.moveDown(10);

    verify(dao, never()).update(any(CharacteristicDto.class), eq(session));
  }
  @Test
  public void not_rename_characteristic_when_renaming_with_same_name() {
    when(dao.selectById(session, 10)).thenReturn(subCharacteristicDto);

    service.rename(10, "Efficiency");

    verify(dao, never()).update(any(CharacteristicDto.class), eq(session));
  }
  @Test
  public void fail_to_move_sub_characteristic() {
    when(dao.selectById(session, 10))
        .thenReturn(new CharacteristicDto().setId(10).setKey("MEMORY_EFFICIENCY").setParentId(1));
    when(dao.selectEnabledRootCharacteristics(session))
        .thenReturn(
            newArrayList(
                new CharacteristicDto().setId(10).setKey("MEMORY_EFFICIENCY").setOrder(2),
                new CharacteristicDto().setId(2).setKey("PORTABILITY").setOrder(3)));

    try {
      service.moveDown(10);
      fail();
    } catch (BadRequestException e) {
      assertThat(e.firstError().getKey()).isEqualTo("Sub characteristics can not be moved.");
    }
    verify(dao, never()).update(any(CharacteristicDto.class), eq(session));
  }
  @Test
  public void move_down() {
    when(dao.selectById(session, 10))
        .thenReturn(new CharacteristicDto().setId(10).setKey("MEMORY_EFFICIENCY").setOrder(2));
    when(dao.selectEnabledRootCharacteristics(session))
        .thenReturn(
            newArrayList(
                new CharacteristicDto().setId(10).setKey("MEMORY_EFFICIENCY").setOrder(2),
                new CharacteristicDto().setId(2).setKey("PORTABILITY").setOrder(3)));

    DebtCharacteristic result = service.moveDown(10);

    verify(dao, times(2)).update(characteristicCaptor.capture(), eq(session));

    assertThat(result.order()).isEqualTo(3);
    assertThat(characteristicCaptor.getAllValues().get(0).getOrder()).isEqualTo(2);
    assertThat(characteristicCaptor.getAllValues().get(0).getUpdatedAt()).isEqualTo(now);
    assertThat(characteristicCaptor.getAllValues().get(1).getOrder()).isEqualTo(3);
    assertThat(characteristicCaptor.getAllValues().get(1).getUpdatedAt()).isEqualTo(now);
  }
  @Test
  public void rename_characteristic() {
    when(dao.selectById(session, 10)).thenReturn(subCharacteristicDto);

    DefaultDebtCharacteristic result =
        (DefaultDebtCharacteristic) service.rename(10, "New Efficiency");

    assertThat(result.key()).isEqualTo("EFFICIENCY");
    assertThat(result.name()).isEqualTo("New Efficiency");
    assertThat(result.updatedAt()).isEqualTo(now);
  }
  @Test
  public void fail_to_move_characteristic_with_no_order() {
    when(dao.selectById(session, 10))
        .thenReturn(new CharacteristicDto().setId(10).setKey("MEMORY_EFFICIENCY").setOrder(null));
    when(dao.selectEnabledRootCharacteristics(session))
        .thenReturn(
            newArrayList(
                new CharacteristicDto().setId(10).setKey("MEMORY_EFFICIENCY").setOrder(2),
                new CharacteristicDto().setId(2).setKey("PORTABILITY").setOrder(3)));

    try {
      service.moveDown(10);
      fail();
    } catch (Exception e) {
      assertThat(e)
          .isInstanceOf(IllegalArgumentException.class)
          .hasMessage("The order of the characteristic 'MEMORY_EFFICIENCY' should not be null");
    }

    verify(dao, never()).update(any(CharacteristicDto.class), eq(session));
  }
  @Test
  public void fail_to_create_sub_characteristic_when_parent_id_is_not_a_root_characteristic() {
    when(dao.selectById(session, 1)).thenReturn(subCharacteristicDto);

    try {
      service.create("Compilation", 1);
      fail();
    } catch (BadRequestException e) {
      assertThat(e.firstError().getKey())
          .isEqualTo("A sub characteristic can not have a sub characteristic as parent.");
    }
  }
  @Test
  public void fail_to_rename_unknown_characteristic() {
    when(dao.selectById(session, 10)).thenReturn(null);

    try {
      service.rename(10, "New Efficiency");
      fail();
    } catch (Exception e) {
      assertThat(e)
          .isInstanceOf(NotFoundException.class)
          .hasMessage("Characteristic with id 10 does not exists.");
    }
  }
  @Test
  public void create_first_characteristic() {
    when(dao.selectMaxCharacteristicOrder(session)).thenReturn(0);

    DefaultDebtCharacteristic result =
        (DefaultDebtCharacteristic) service.create("Portability", null);

    assertThat(result.id()).isEqualTo(currentId);
    assertThat(result.key()).isEqualTo("PORTABILITY");
    assertThat(result.name()).isEqualTo("Portability");
    assertThat(result.order()).isEqualTo(1);
    assertThat(result.createdAt()).isEqualTo(now);
  }
  @Test
  public void fail_to_create_sub_characteristic_when_parent_does_not_exists() {
    when(dao.selectById(session, 1)).thenReturn(null);

    try {
      service.create("Compilation", 1);
      fail();
    } catch (Exception e) {
      assertThat(e)
          .isInstanceOf(NotFoundException.class)
          .hasMessage("Characteristic with id 1 does not exists.");
    }
  }
  @Test
  public void create_sub_characteristic() {
    when(dao.selectById(session, 1)).thenReturn(characteristicDto);

    DefaultDebtCharacteristic result =
        (DefaultDebtCharacteristic) service.create("Compilation name", 1);

    assertThat(result.id()).isEqualTo(currentId);
    assertThat(result.key()).isEqualTo("COMPILATION_NAME");
    assertThat(result.name()).isEqualTo("Compilation name");
    assertThat(result.parentId()).isEqualTo(1);
    assertThat(result.createdAt()).isEqualTo(now);
  }
  @Test
  public void delete_sub_characteristic() {
    DbSession batchSession = mock(DbSession.class);
    when(dbClient.openSession(true)).thenReturn(batchSession);

    when(ruleDao.selectRulesByDebtSubCharacteristicId(batchSession, 2))
        .thenReturn(
            newArrayList(
                new RuleDto()
                    .setSubCharacteristicId(2)
                    .setRemediationFunction(DebtRemediationFunction.Type.LINEAR_OFFSET.toString())
                    .setRemediationCoefficient("2h")
                    .setRemediationOffset("5min")
                    .setDefaultSubCharacteristicId(10)
                    .setDefaultRemediationFunction(
                        DebtRemediationFunction.Type.LINEAR_OFFSET.toString())
                    .setDefaultRemediationCoefficient("4h")
                    .setDefaultRemediationOffset("15min")));
    when(dao.selectById(batchSession, 2)).thenReturn(subCharacteristicDto);

    service.delete(2);

    verify(ruleDao).update(eq(batchSession), ruleCaptor.capture());

    RuleDto ruleDto = ruleCaptor.getValue();
    assertThat(ruleDto.getUpdatedAt()).isEqualTo(now);

    // Overridden debt data are disabled
    assertThat(ruleDto.getSubCharacteristicId()).isEqualTo(-1);
    assertThat(ruleDto.getRemediationFunction()).isNull();
    assertThat(ruleDto.getRemediationCoefficient()).isNull();
    assertThat(ruleDto.getRemediationOffset()).isNull();

    // Default debt data should not be touched
    assertThat(ruleDto.getDefaultSubCharacteristicId()).isEqualTo(10);
    assertThat(ruleDto.getDefaultRemediationFunction()).isEqualTo("LINEAR_OFFSET");
    assertThat(ruleDto.getDefaultRemediationCoefficient()).isEqualTo("4h");
    assertThat(ruleDto.getDefaultRemediationOffset()).isEqualTo("15min");

    verify(dao).update(characteristicCaptor.capture(), eq(batchSession));
    CharacteristicDto characteristicDto = characteristicCaptor.getValue();

    // Sub characteristic is disable
    assertThat(characteristicDto.getId()).isEqualTo(2);
    assertThat(characteristicDto.isEnabled()).isFalse();
    assertThat(characteristicDto.getUpdatedAt()).isEqualTo(now);
  }
  @Test
  public void not_delete_already_disabled_characteristic() {
    DbSession batchSession = mock(DbSession.class);
    when(dbClient.openSession(true)).thenReturn(batchSession);

    when(dao.selectById(batchSession, 1))
        .thenReturn(
            new CharacteristicDto()
                .setId(1)
                .setKey("MEMORY_EFFICIENCY")
                .setName("Memory use")
                .setOrder(2)
                .setEnabled(false));

    service.delete(1);

    verify(dao, never()).update(any(CharacteristicDto.class), eq(batchSession));
  }