Feature/fix sonar warnings (#1226)

* Fixed sonar warnings

- "Cognitive Complexity"
- "Do not use replaceAll when not using a regex"
- java:S5869 - Character classes in regular expressions should not contain the same character twice
- Improved bad name
- Typos
- reduced code duplications
- Replaced hand-made wait-utility with Awaitility
- Log messages
- Duplicate code
- Typos
- Removed Thread.sleep, instead relaxed check condition
- Removed use of deprecated API
- Removed use of deprecated API
- Added supress-warnings as I do not see a better way to write the tests
- Removed Thread.sleep / redundant functionality to Awaitility
- Fixed other warnings (use isZero, isEmpty, hasToString)
- Removed/Reduced duplicate code
- Added generics
- Fixed asserts
- removed: field.setAccessible(true) actually should not be needed for public static fields!
- Too long constructor passes arguments in wrong order - how surprisingly...
- Clean-up use of varargs arguments
- Fixed regex
- Fixed typos and other minor stuff
- Making public constructors protected in abstract classes
- Swapped expected and asserted argument
- volatile not enough for syncing threads
- volatile not enough for syncing threads
- out-commented code
- Made regex not-greedy, added tests for verification
- Avoid exposure of thread-local member var

Signed-off-by: Peter Vigier <Peter.Vigier@bosch.io>

* Fixed Sonar warnings

* License header fix

Signed-off-by: Peter Vigier <Peter.Vigier@bosch.io>

* License header fix #2

Signed-off-by: Peter Vigier <Peter.Vigier@bosch.io>

* Fixing review findings

Signed-off-by: Peter Vigier <Peter.Vigier@bosch.io>

* Fixing tests

- Fixed '&' usage in javadoc and typos
- Fixing some warnings

Signed-off-by: Peter Vigier <Peter.Vigier@bosch.io>
This commit is contained in:
Peter Vigier
2022-01-31 21:59:46 +01:00
committed by GitHub
parent 5443b5df9c
commit 44a85f20eb
98 changed files with 2583 additions and 2702 deletions

View File

@@ -45,11 +45,11 @@ import io.qameta.allure.Story;
*/
@Feature("Component Tests - Direct Device Integration API")
@Story("Cancel Action Resource")
public class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
@Test
@Description("Tests that the cancel action resource can be used with CBOR.")
public void cancelActionCbor() throws Exception {
void cancelActionCbor() throws Exception {
final DistributionSet ds = testdataFactory.createDistributionSet("");
testdataFactory.createTarget();
final Long actionId = getFirstAssignedActionId(
@@ -75,7 +75,7 @@ public class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
@Test
@Description("Test of the controller can continue a started update even after a cancel command if it so desires.")
public void rootRsCancelActionButContinueAnyway() throws Exception {
void rootRsCancelActionButContinueAnyway() throws Exception {
// prepare test data
final DistributionSet ds = testdataFactory.createDistributionSet("");
final Target savedTarget = testdataFactory.createTarget();
@@ -130,14 +130,14 @@ public class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
@Test
@Description("Test for cancel operation of a update action.")
public void rootRsCancelAction() throws Exception {
void rootRsCancelAction() throws Exception {
final DistributionSet ds = testdataFactory.createDistributionSet("");
final Target savedTarget = testdataFactory.createTarget();
final Long actionId = getFirstAssignedActionId(
assignDistributionSet(ds.getId(), savedTarget.getControllerId()));
long current = System.currentTimeMillis();
final long timeBeforeFirstPoll = System.currentTimeMillis();
mvc.perform(get("/{tenant}/controller/v1/{controller}", tenantAware.getCurrentTenant(),
TestdataFactory.DEFAULT_CONTROLLER_ID).accept(MediaTypes.HAL_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
@@ -146,13 +146,9 @@ public class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
.andExpect(jsonPath("$._links.deploymentBase.href",
startsWith("http://localhost/" + tenantAware.getCurrentTenant() + "/controller/v1/"
+ TestdataFactory.DEFAULT_CONTROLLER_ID + "/deploymentBase/" + actionId)));
Thread.sleep(1); // is required: otherwise processing the next line is
// often too fast and
// the following assert will fail
final long timeAfterFirstPoll = System.currentTimeMillis() + 1;
assertThat(targetManagement.getByControllerID(TestdataFactory.DEFAULT_CONTROLLER_ID).get().getLastTargetQuery())
.isLessThanOrEqualTo(System.currentTimeMillis());
assertThat(targetManagement.getByControllerID(TestdataFactory.DEFAULT_CONTROLLER_ID).get().getLastTargetQuery())
.isGreaterThanOrEqualTo(current);
.isBetween(timeBeforeFirstPoll, timeAfterFirstPoll);
// Retrieved is reported
@@ -171,7 +167,7 @@ public class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
assertThat(activeActionsByTarget).hasSize(1);
assertThat(activeActionsByTarget.get(0).getStatus()).isEqualTo(Status.CANCELING);
current = System.currentTimeMillis();
final long timeBefore2ndPoll = System.currentTimeMillis();
mvc.perform(get("/{tenant}/controller/v1/{controller}", tenantAware.getCurrentTenant(),
TestdataFactory.DEFAULT_CONTROLLER_ID)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(MediaTypes.HAL_JSON))
@@ -179,17 +175,10 @@ public class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
.andExpect(jsonPath("$._links.cancelAction.href",
equalTo("http://localhost/" + tenantAware.getCurrentTenant() + "/controller/v1/"
+ TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/" + cancelAction.getId())));
Thread.sleep(1); // is required: otherwise processing the next line is
// often too fast and
// the following assert will fail
final long timeAfter2ndPoll = System.currentTimeMillis() + 1;
assertThat(targetManagement.getByControllerID(TestdataFactory.DEFAULT_CONTROLLER_ID).get().getLastTargetQuery())
.isLessThanOrEqualTo(System.currentTimeMillis());
assertThat(targetManagement.getByControllerID(TestdataFactory.DEFAULT_CONTROLLER_ID).get().getLastTargetQuery())
.isGreaterThanOrEqualTo(current);
.isBetween(timeBefore2ndPoll, timeAfter2ndPoll);
current = System.currentTimeMillis();
assertThat(targetManagement.getByControllerID(TestdataFactory.DEFAULT_CONTROLLER_ID).get().getLastTargetQuery())
.isLessThanOrEqualTo(System.currentTimeMillis());
mvc.perform(get("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId(), tenantAware.getCurrentTenant()).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
@@ -208,7 +197,7 @@ public class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
activeActionsByTarget = deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())
.getContent();
assertThat(activeActionsByTarget).hasSize(0);
assertThat(activeActionsByTarget).isEmpty();
final Action canceledAction = deploymentManagement.findAction(cancelAction.getId()).get();
assertThat(canceledAction.isActive()).isFalse();
assertThat(canceledAction.getStatus()).isEqualTo(Status.CANCELED);
@@ -217,7 +206,7 @@ public class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
@Test
@Description("Tests various bad requests and if the server handles them as expected.")
public void badCancelAction() throws Exception {
void badCancelAction() throws Exception {
// not allowed methods
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/1",
@@ -258,7 +247,7 @@ public class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
@Test
@Description("Tests the feedback channel of the cancel operation.")
public void rootRsCancelActionFeedback() throws Exception {
void rootRsCancelActionFeedback() throws Exception {
final DistributionSet ds = testdataFactory.createDistributionSet("");
@@ -327,12 +316,12 @@ public class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
assertThat(deploymentManagement.countActionStatusAll()).isEqualTo(8);
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(0);
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).isEmpty();
}
@Test
@Description("Tests the feeback chanel of for multiple open cancel operations on the same target.")
public void multipleCancelActionFeedback() throws Exception {
void multipleCancelActionFeedback() throws Exception {
final DistributionSet ds = testdataFactory.createDistributionSet("", true);
final DistributionSet ds2 = testdataFactory.createDistributionSet("2", true);
final DistributionSet ds3 = testdataFactory.createDistributionSet("3", true);
@@ -443,13 +432,13 @@ public class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
assertThat(deploymentManagement.countActionStatusAll()).isEqualTo(13);
// final status
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(0);
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).isEmpty();
assertThat(deploymentManagement.countActionsByTarget(savedTarget.getControllerId())).isEqualTo(3);
}
@Test
@Description("Tests the feeback channel closing for too many feedbacks, i.e. denial of service prevention.")
public void tooMuchCancelActionFeedback() throws Exception {
void tooMuchCancelActionFeedback() throws Exception {
testdataFactory.createTarget();
final DistributionSet ds = testdataFactory.createDistributionSet("");
@@ -477,9 +466,9 @@ public class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
@Test
@Description("test the correct rejection of various invalid feedback requests")
public void badCancelActionFeedback() throws Exception {
void badCancelActionFeedback() throws Exception {
final Action cancelAction = createCancelAction(TestdataFactory.DEFAULT_CONTROLLER_ID);
final Action cancelAction2 = createCancelAction("4715");
createCancelAction("4715");
// not allowed methods
mvc.perform(put("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
@@ -524,12 +513,11 @@ public class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isBadRequest());
// finally get it right :)
// finaly, get it right :)
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
.content(JsonBuilder.cancelActionFeedback(cancelAction.getId().toString(), "closed"))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
}
}

View File

@@ -9,6 +9,10 @@
package org.eclipse.hawkbit.ddi.rest.resource;
import static org.assertj.core.api.Assertions.assertThat;
import static org.eclipse.hawkbit.repository.test.util.TargetTestData.ATTRIBUTE_KEY_TOO_LONG;
import static org.eclipse.hawkbit.repository.test.util.TargetTestData.ATTRIBUTE_KEY_VALID;
import static org.eclipse.hawkbit.repository.test.util.TargetTestData.ATTRIBUTE_VALUE_TOO_LONG;
import static org.eclipse.hawkbit.repository.test.util.TargetTestData.ATTRIBUTE_VALUE_VALID;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
@@ -21,6 +25,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.NoSuchElementException;
import org.eclipse.hawkbit.ddi.rest.api.DdiRestConstants;
import org.eclipse.hawkbit.exception.SpServerError;
@@ -40,41 +45,39 @@ import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.qameta.allure.Step;
import io.qameta.allure.Story;
/**
* Test config data from the controller.
*/
@ActiveProfiles({ "im", "test" })
@Feature("Component Tests - Direct Device Integration API")
@Story("Config Data Resource")
public class DdiConfigDataTest extends AbstractDDiApiIntegrationTest {
class DdiConfigDataTest extends AbstractDDiApiIntegrationTest {
private static final String KEY_TOO_LONG = generateRandomStringWithLength(Target.CONTROLLER_ATTRIBUTE_KEY_SIZE + 1);
private static final String KEY_VALID = generateRandomStringWithLength(Target.CONTROLLER_ATTRIBUTE_KEY_SIZE);
private static final String VALUE_TOO_LONG = generateRandomStringWithLength(
Target.CONTROLLER_ATTRIBUTE_VALUE_SIZE + 1);
private static final String VALUE_VALID = generateRandomStringWithLength(Target.CONTROLLER_ATTRIBUTE_VALUE_SIZE);
public static final String TARGET1_ID = "4717";
public static final String TARGET1_CONFIGDATA_PATH = "/{tenant}/controller/v1/" + TARGET1_ID + "/configData";
public static final String TARGET2_ID = "4718";
public static final String TARGET2_CONFIGDATA_PATH = "/{tenant}/controller/v1/" + TARGET2_ID + "/configData";
@Test
@Description("Verify that config data can be uploaded as CBOR")
public void putConfigDataAsCbor() throws Exception {
testdataFactory.createTarget("4717");
void putConfigDataAsCbor() throws Exception {
testdataFactory.createTarget(TARGET1_ID);
final Map<String, String> attributes = new HashMap<>();
attributes.put(KEY_VALID, VALUE_VALID);
attributes.put(ATTRIBUTE_KEY_VALID, ATTRIBUTE_VALUE_VALID);
mvc.perform(put("/{tenant}/controller/v1/4717/configData", tenantAware.getCurrentTenant())
mvc.perform(put(TARGET1_CONFIGDATA_PATH, tenantAware.getCurrentTenant())
.content(jsonToCbor(JsonBuilder.configData(attributes).toString()))
.contentType(DdiRestConstants.MEDIA_TYPE_CBOR)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk());
assertThat(targetManagement.getControllerAttributes("4717")).isEqualTo(attributes);
assertThat(targetManagement.getControllerAttributes(TARGET1_ID)).isEqualTo(attributes);
}
@Test
@Description("We verify that the config data (i.e. device attributes like serial number, hardware revision etc.) "
+ "are requested only once from the device.")
@SuppressWarnings("squid:S2925")
public void requestConfigDataIfEmpty() throws Exception {
void requestConfigDataIfEmpty() throws Exception {
final Target savedTarget = testdataFactory.createTarget("4712");
final long current = System.currentTimeMillis();
@@ -88,9 +91,11 @@ public class DdiConfigDataTest extends AbstractDDiApiIntegrationTest {
Thread.sleep(1); // is required: otherwise processing the next line is
// often too fast and
// the following assert will fail
assertThat(targetManagement.getByControllerID("4712").get().getLastTargetQuery())
assertThat(targetManagement.getByControllerID("4712").orElseThrow(NoSuchElementException::new)
.getLastTargetQuery())
.isLessThanOrEqualTo(System.currentTimeMillis());
assertThat(targetManagement.getByControllerID("4712").get().getLastTargetQuery())
assertThat(targetManagement.getByControllerID("4712").orElseThrow(NoSuchElementException::new)
.getLastTargetQuery())
.isGreaterThanOrEqualTo(current);
final Map<String, String> attributes = Maps.newHashMapWithExpectedSize(1);
@@ -113,44 +118,44 @@ public class DdiConfigDataTest extends AbstractDDiApiIntegrationTest {
@Test
@Description("We verify that the config data (i.e. device attributes like serial number, hardware revision etc.) "
+ "can be uploaded correctly by the controller.")
public void putConfigData() throws Exception {
testdataFactory.createTarget("4717");
void putConfigData() throws Exception {
testdataFactory.createTarget(TARGET1_ID);
// initial
final Map<String, String> attributes = new HashMap<>();
attributes.put(KEY_VALID, VALUE_VALID);
attributes.put(ATTRIBUTE_KEY_VALID, ATTRIBUTE_VALUE_VALID);
mvc.perform(put("/{tenant}/controller/v1/4717/configData", tenantAware.getCurrentTenant())
mvc.perform(put(TARGET1_CONFIGDATA_PATH, tenantAware.getCurrentTenant())
.content(JsonBuilder.configData(attributes).toString()).contentType(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
assertThat(targetManagement.getControllerAttributes("4717")).isEqualTo(attributes);
assertThat(targetManagement.getControllerAttributes(TARGET1_ID)).isEqualTo(attributes);
// update
attributes.put("sdsds", "123412");
mvc.perform(put("/{tenant}/controller/v1/4717/configData", tenantAware.getCurrentTenant())
mvc.perform(put(TARGET1_CONFIGDATA_PATH, tenantAware.getCurrentTenant())
.content(JsonBuilder.configData(attributes).toString()).contentType(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
assertThat(targetManagement.getControllerAttributes("4717")).isEqualTo(attributes);
assertThat(targetManagement.getControllerAttributes(TARGET1_ID)).isEqualTo(attributes);
}
@Test
@Description("We verify that the config data (i.e. device attributes like serial number, hardware revision etc.) "
+ "upload quota is enforced to protect the server from malicious attempts.")
public void putTooMuchConfigData() throws Exception {
testdataFactory.createTarget("4717");
void putTooMuchConfigData() throws Exception {
testdataFactory.createTarget(TARGET1_ID);
// initial
Map<String, String> attributes = new HashMap<>();
for (int i = 0; i < quotaManagement.getMaxAttributeEntriesPerTarget(); i++) {
attributes.put("dsafsdf" + i, "sdsds" + i);
}
mvc.perform(put("/{tenant}/controller/v1/4717/configData", tenantAware.getCurrentTenant())
mvc.perform(put(TARGET1_CONFIGDATA_PATH, tenantAware.getCurrentTenant())
.content(JsonBuilder.configData(attributes).toString()).contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
attributes = new HashMap<>();
attributes.put("on too many", "sdsds");
mvc.perform(put("/{tenant}/controller/v1/4717/configData", tenantAware.getCurrentTenant())
mvc.perform(put(TARGET1_CONFIGDATA_PATH, tenantAware.getCurrentTenant())
.content(JsonBuilder.configData(attributes).toString()).contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isForbidden())
.andExpect(jsonPath("$.exceptionClass", equalTo(AssignmentQuotaExceededException.class.getName())))
@@ -161,7 +166,7 @@ public class DdiConfigDataTest extends AbstractDDiApiIntegrationTest {
@Test
@Description("We verify that the config data (i.e. device attributes like serial number, hardware revision etc.) "
+ "resource behaves as expected in case of invalid request attempts.")
public void badConfigData() throws Exception {
void badConfigData() throws Exception {
testdataFactory.createTarget("4712");
// not allowed methods
@@ -195,23 +200,17 @@ public class DdiConfigDataTest extends AbstractDDiApiIntegrationTest {
@Test
@Description("Verifies that invalid config data attributes are handled correctly.")
public void putConfigDataWithInvalidAttributes() throws Exception {
void putConfigDataWithInvalidAttributes() throws Exception {
// create a target
final String controllerId = "4718";
testdataFactory.createTarget(controllerId);
final String configDataPath = "/{tenant}/controller/v1/" + controllerId + "/configData";
putAndVerifyConfigDataWithKeyTooLong(configDataPath);
putAndVerifyConfigDataWithValueTooLong(configDataPath);
testdataFactory.createTarget(TARGET2_ID);
putAndVerifyConfigDataWithKeyTooLong();
putAndVerifyConfigDataWithValueTooLong();
}
@Step
private void putAndVerifyConfigDataWithKeyTooLong(final String configDataPath) throws Exception {
final Map<String, String> attributes = Collections.singletonMap(KEY_TOO_LONG, VALUE_VALID);
mvc.perform(put(configDataPath, tenantAware.getCurrentTenant())
private void putAndVerifyConfigDataWithKeyTooLong() throws Exception {
final Map<String, String> attributes = Collections.singletonMap(ATTRIBUTE_KEY_TOO_LONG, ATTRIBUTE_VALUE_VALID);
mvc.perform(put(DdiConfigDataTest.TARGET2_CONFIGDATA_PATH, tenantAware.getCurrentTenant())
.content(JsonBuilder.configData(attributes).toString()).contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.exceptionClass", equalTo(InvalidTargetAttributeException.class.getName())))
@@ -219,11 +218,9 @@ public class DdiConfigDataTest extends AbstractDDiApiIntegrationTest {
}
@Step
private void putAndVerifyConfigDataWithValueTooLong(final String configDataPath) throws Exception {
final Map<String, String> attributes = Collections.singletonMap(KEY_VALID, VALUE_TOO_LONG);
mvc.perform(put(configDataPath, tenantAware.getCurrentTenant())
private void putAndVerifyConfigDataWithValueTooLong() throws Exception {
final Map<String, String> attributes = Collections.singletonMap(ATTRIBUTE_KEY_VALID, ATTRIBUTE_VALUE_TOO_LONG);
mvc.perform(put(DdiConfigDataTest.TARGET2_CONFIGDATA_PATH, tenantAware.getCurrentTenant())
.content(JsonBuilder.configData(attributes).toString()).contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.exceptionClass", equalTo(InvalidTargetAttributeException.class.getName())))
@@ -232,139 +229,133 @@ public class DdiConfigDataTest extends AbstractDDiApiIntegrationTest {
@Test
@Description("Verify that config data (device attributes) can be updated by the controller using different update modes (merge, replace, remove).")
public void putConfigDataWithDifferentUpdateModes() throws Exception {
void putConfigDataWithDifferentUpdateModes() throws Exception {
// create a target
final String controllerId = "4717";
testdataFactory.createTarget(controllerId);
final String configDataPath = "/{tenant}/controller/v1/" + controllerId + "/configData";
testdataFactory.createTarget(TARGET1_ID);
// no update mode
putConfigDataWithoutUpdateMode(controllerId, configDataPath);
putConfigDataWithoutUpdateMode();
// update mode REPLACE
putConfigDataWithUpdateModeReplace(controllerId, configDataPath);
putConfigDataWithUpdateModeReplace();
// update mode MERGE
putConfigDataWithUpdateModeMerge(controllerId, configDataPath);
putConfigDataWithUpdateModeMerge();
// update mode REMOVE
putConfigDataWithUpdateModeRemove(controllerId, configDataPath);
putConfigDataWithUpdateModeRemove();
// invalid update mode
putConfigDataWithInvalidUpdateMode(configDataPath);
putConfigDataWithInvalidUpdateMode();
}
@Step
private void putConfigDataWithInvalidUpdateMode(final String configDataPath) throws Exception {
private void putConfigDataWithInvalidUpdateMode() throws Exception {
// create some attriutes
final Map<String, String> attributes = new HashMap<>();
attributes.put("k0", "v0");
attributes.put("k1", "v1");
// use an invalid update mode
mvc.perform(put(configDataPath, tenantAware.getCurrentTenant())
mvc.perform(put(DdiConfigDataTest.TARGET1_CONFIGDATA_PATH, tenantAware.getCurrentTenant())
.content(JsonBuilder.configData(attributes, "KJHGKJHGKJHG").toString())
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isBadRequest());
}
@Step
private void putConfigDataWithUpdateModeRemove(final String controllerId, final String configDataPath)
throws Exception {
private void putConfigDataWithUpdateModeRemove() throws Exception {
// get the current attributes
final int previousSize = targetManagement.getControllerAttributes(controllerId).size();
final int previousSize = targetManagement.getControllerAttributes(DdiConfigDataTest.TARGET1_ID).size();
// update the attributes using update mode REMOVE
final Map<String, String> removeAttributes = new HashMap<>();
removeAttributes.put("k1", "foo");
removeAttributes.put("k3", "bar");
mvc.perform(put(configDataPath, tenantAware.getCurrentTenant())
mvc.perform(put(DdiConfigDataTest.TARGET1_CONFIGDATA_PATH, tenantAware.getCurrentTenant())
.content(JsonBuilder.configData(removeAttributes, "remove").toString())
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk());
// verify attribute removal
final Map<String, String> updatedAttributes = targetManagement.getControllerAttributes(controllerId);
assertThat(updatedAttributes.size()).isEqualTo(previousSize - 2);
final Map<String, String> updatedAttributes = targetManagement
.getControllerAttributes(DdiConfigDataTest.TARGET1_ID);
assertThat(updatedAttributes).hasSize(previousSize - 2);
assertThat(updatedAttributes).doesNotContainKeys("k1", "k3");
}
@Step
private void putConfigDataWithUpdateModeMerge(final String controllerId, final String configDataPath)
private void putConfigDataWithUpdateModeMerge()
throws Exception {
// get the current attributes
final Map<String, String> attributes = new HashMap<>(targetManagement.getControllerAttributes(controllerId));
final Map<String, String> attributes = new HashMap<>(
targetManagement.getControllerAttributes(DdiConfigDataTest.TARGET1_ID));
// update the attributes using update mode MERGE
final Map<String, String> mergeAttributes = new HashMap<>();
mergeAttributes.put("k1", "v1_modified_again");
mergeAttributes.put("k4", "v4");
mvc.perform(put(configDataPath, tenantAware.getCurrentTenant())
mvc.perform(put(DdiConfigDataTest.TARGET1_CONFIGDATA_PATH, tenantAware.getCurrentTenant())
.content(JsonBuilder.configData(mergeAttributes, "merge").toString())
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk());
// verify attribute merge
final Map<String, String> updatedAttributes = targetManagement.getControllerAttributes(controllerId);
assertThat(updatedAttributes.size()).isEqualTo(4);
final Map<String, String> updatedAttributes = targetManagement
.getControllerAttributes(DdiConfigDataTest.TARGET1_ID);
assertThat(updatedAttributes).hasSize(4);
assertThat(updatedAttributes).containsAllEntriesOf(mergeAttributes);
assertThat(updatedAttributes.get("k1")).isEqualTo("v1_modified_again");
assertThat(updatedAttributes).containsEntry("k1", "v1_modified_again");
attributes.keySet().forEach(assertThat(updatedAttributes)::containsKey);
}
@Step
private void putConfigDataWithUpdateModeReplace(final String controllerId, final String configDataPath)
private void putConfigDataWithUpdateModeReplace()
throws Exception {
// get the current attributes
final Map<String, String> attributes = new HashMap<>(targetManagement.getControllerAttributes(controllerId));
final Map<String, String> attributes = new HashMap<>(
targetManagement.getControllerAttributes(DdiConfigDataTest.TARGET1_ID));
// update the attributes using update mode REPLACE
final Map<String, String> replacementAttributes = new HashMap<>();
replacementAttributes.put("k1", "v1_modified");
replacementAttributes.put("k2", "v2");
replacementAttributes.put("k3", "v3");
mvc.perform(put(configDataPath, tenantAware.getCurrentTenant())
mvc.perform(put(DdiConfigDataTest.TARGET1_CONFIGDATA_PATH, tenantAware.getCurrentTenant())
.content(JsonBuilder.configData(replacementAttributes, "replace").toString())
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk());
// verify attribute replacement
final Map<String, String> updatedAttributes = targetManagement.getControllerAttributes(controllerId);
assertThat(updatedAttributes.size()).isEqualTo(replacementAttributes.size());
final Map<String, String> updatedAttributes = targetManagement
.getControllerAttributes(DdiConfigDataTest.TARGET1_ID);
assertThat(updatedAttributes).hasSize(replacementAttributes.size());
assertThat(updatedAttributes).containsAllEntriesOf(replacementAttributes);
assertThat(updatedAttributes.get("k1")).isEqualTo("v1_modified");
assertThat(updatedAttributes).containsEntry("k1", "v1_modified");
attributes.entrySet().forEach(assertThat(updatedAttributes)::doesNotContain);
}
@Step
private void putConfigDataWithoutUpdateMode(final String controllerId, final String configDataPath)
private void putConfigDataWithoutUpdateMode()
throws Exception {
// create some attriutes
final Map<String, String> attributes = new HashMap<>();
attributes.put("k0", "v0");
attributes.put("k1", "v1");
// set the initial attributes
mvc.perform(put(configDataPath, tenantAware.getCurrentTenant())
mvc.perform(put(DdiConfigDataTest.TARGET1_CONFIGDATA_PATH, tenantAware.getCurrentTenant())
.content(JsonBuilder.configData(attributes).toString()).contentType(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
// verify the initial parameters
final Map<String, String> updatedAttributes = targetManagement.getControllerAttributes(controllerId);
assertThat(updatedAttributes.size()).isEqualTo(attributes.size());
assertThat(updatedAttributes).containsAllEntriesOf(attributes);
final Map<String, String> updatedAttributes = targetManagement
.getControllerAttributes(DdiConfigDataTest.TARGET1_ID);
assertThat(updatedAttributes).containsExactlyEntriesOf(attributes);
}
}

View File

@@ -77,7 +77,7 @@ import io.qameta.allure.Story;
*/
@Feature("Component Tests - Direct Device Integration API")
@Story("Root Poll Resource")
public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
private static final String TARGET_COMPLETED_INSTALLATION_MSG = "Target completed installation.";
private static final String TARGET_PROCEEDING_INSTALLATION_MSG = "Target proceeding installation.";
@@ -87,7 +87,7 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
@Test
@Description("Ensure that the root poll resource is available as CBOR")
public void rootPollResourceCbor() throws Exception {
void rootPollResourceCbor() throws Exception {
mvc.perform(get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), 4711)
.accept(DdiRestConstants.MEDIA_TYPE_CBOR)).andDo(MockMvcResultPrinter.print())
.andExpect(content().contentType(DdiRestConstants.MEDIA_TYPE_CBOR)).andExpect(status().isOk());
@@ -95,7 +95,7 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
@Test
@Description("Ensures that the API returns JSON when no Accept header is specified by the client.")
public void apiReturnsJSONByDefault() throws Exception {
void apiReturnsJSONByDefault() throws Exception {
final MvcResult result = mvc.perform(get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), 4711))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(MediaTypes.HAL_JSON)).andReturn();
@@ -113,7 +113,7 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
@Expect(type = TargetPollEvent.class, count = 1),
@Expect(type = DistributionSetTypeCreatedEvent.class, count = 3),
@Expect(type = SoftwareModuleTypeCreatedEvent.class, count = 2) })
public void targetCannotBeRegisteredIfTenantDoesNotExistsButWhenExists() throws Exception {
void targetCannotBeRegisteredIfTenantDoesNotExistsButWhenExists() throws Exception {
mvc.perform(get("/default-tenant/", tenantAware.getCurrentTenant())).andDo(MockMvcResultPrinter.print())
.andExpect(status().isNotFound());
@@ -137,14 +137,13 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
SpPermission.CREATE_TARGET }, allSpPermissions = false)
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1) })
public void targetPollDoesNotModifyAuditData() throws Exception {
void targetPollDoesNotModifyAuditData() throws Exception {
// create target first with "knownPrincipal" user and audit data
final String knownTargetControllerId = "target1";
final String knownCreatedBy = "knownPrincipal";
testdataFactory.createTarget(knownTargetControllerId);
final Target findTargetByControllerID = targetManagement.getByControllerID(knownTargetControllerId).get();
assertThat(findTargetByControllerID.getCreatedBy()).isEqualTo(knownCreatedBy);
assertThat(findTargetByControllerID.getCreatedAt()).isNotNull();
// make a poll, audit information should not be changed, run as
// controller principal!
@@ -165,7 +164,7 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
@Test
@Description("Ensures that server returns a not found response in case of empty controller ID.")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
public void rootRsWithoutId() throws Exception {
void rootRsWithoutId() throws Exception {
mvc.perform(get("/controller/v1/")).andDo(MockMvcResultPrinter.print()).andExpect(status().isNotFound());
}
@@ -173,7 +172,7 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
@Description("Ensures that the system creates a new target in plug and play manner, i.e. target is authenticated but does not exist yet.")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetPollEvent.class, count = 1) })
public void rootRsPlugAndPlay() throws Exception {
void rootRsPlugAndPlay() throws Exception {
final long current = System.currentTimeMillis();
String controllerId = "4711";
@@ -204,7 +203,7 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetPollEvent.class, count = 1),
@Expect(type = TenantConfigurationCreatedEvent.class, count = 1) })
public void pollWithModifiedGlobalPollingTime() throws Exception {
void pollWithModifiedGlobalPollingTime() throws Exception {
WithSpringAuthorityRule.runAs(WithSpringAuthorityRule.withUser("tenantadmin", HAS_AUTH_TENANT_CONFIGURATION), () -> {
tenantConfigurationManagement.addOrUpdateConfiguration(TenantConfigurationKey.POLLING_TIME_INTERVAL,
"00:02:00");
@@ -230,7 +229,7 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
@Expect(type = ActionCreatedEvent.class, count = 2),
@Expect(type = TargetAttributesRequestedEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 6) })
public void rootRsNotModified() throws Exception {
void rootRsNotModified() throws Exception {
String controllerId = "4711";
final String etag = mvc.perform(get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), controllerId))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
@@ -308,7 +307,7 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
+ "UNKNOWN to REGISTERED when the target polls for the first time.")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1) })
public void rootRsPrecommissioned() throws Exception {
void rootRsPrecommissioned() throws Exception {
String controllerId = "4711";
testdataFactory.createTarget(controllerId);
@@ -334,7 +333,7 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
@Description("Ensures that the source IP address of the polling target is correctly stored in repository")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetPollEvent.class, count = 1) })
public void rootRsPlugAndPlayIpAddress() throws Exception {
void rootRsPlugAndPlayIpAddress() throws Exception {
// test
final String knownControllerId1 = "0815";
final long create = System.currentTimeMillis();
@@ -361,7 +360,7 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
@Description("Ensures that the source IP address of the polling target is not stored in repository if disabled")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetPollEvent.class, count = 1) })
public void rootRsIpAddressNotStoredIfDisabled() throws Exception {
void rootRsIpAddressNotStoredIfDisabled() throws Exception {
securityProperties.getClients().setTrackRemoteIp(false);
// test
@@ -384,7 +383,7 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
@Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 2),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3) })
public void tryToFinishAnUpdateProcessAfterItHasBeenFinished() throws Exception {
void tryToFinishAnUpdateProcessAfterItHasBeenFinished() throws Exception {
final DistributionSet ds = testdataFactory.createDistributionSet("");
Target savedTarget = testdataFactory.createTarget("911");
savedTarget = getFirstAssignedTarget(assignDistributionSet(ds.getId(), savedTarget.getControllerId()));
@@ -409,7 +408,7 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
@Expect(type = TargetUpdatedEvent.class, count = 6), @Expect(type = TargetPollEvent.class, count = 4),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3),
@Expect(type = TargetAttributesRequestedEvent.class, count = 1) })
public void attributeUpdateRequestSendingAfterSuccessfulDeployment() throws Exception {
void attributeUpdateRequestSendingAfterSuccessfulDeployment() throws Exception {
final DistributionSet ds = testdataFactory.createDistributionSet("1");
final Target savedTarget = testdataFactory.createTarget("922");
final Map<String, String> attributes = Collections.singletonMap("AttributeKey", "AttributeValue");
@@ -487,7 +486,7 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
@Expect(type = TargetUpdatedEvent.class, count = 2),
@Expect(type = TargetAttributesRequestedEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3) })
public void testActionHistoryCount() throws Exception {
void testActionHistoryCount() throws Exception {
final DistributionSet ds = testdataFactory.createDistributionSet("");
Target savedTarget = testdataFactory.createTarget("911");
savedTarget = getFirstAssignedTarget(assignDistributionSet(ds.getId(), savedTarget.getControllerId()));
@@ -524,7 +523,7 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
@Expect(type = TargetUpdatedEvent.class, count = 2),
@Expect(type = TargetAttributesRequestedEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3) })
public void testActionHistoryZeroInput() throws Exception {
void testActionHistoryZeroInput() throws Exception {
final DistributionSet ds = testdataFactory.createDistributionSet("");
Target savedTarget = testdataFactory.createTarget("911");
savedTarget = getFirstAssignedTarget(assignDistributionSet(ds.getId(), savedTarget.getControllerId()));
@@ -561,7 +560,7 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
@Expect(type = TargetUpdatedEvent.class, count = 2),
@Expect(type = TargetAttributesRequestedEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3) })
public void testActionHistoryNegativeInput() throws Exception {
void testActionHistoryNegativeInput() throws Exception {
final DistributionSet ds = testdataFactory.createDistributionSet("");
Target savedTarget = testdataFactory.createTarget("911");
savedTarget = getFirstAssignedTarget(assignDistributionSet(ds.getId(), savedTarget.getControllerId()));
@@ -591,7 +590,7 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
@Test
@Description("Test the polling time based on different maintenance window start and end time.")
public void sleepTimeResponseForDifferentMaintenanceWindowParameters() throws Exception {
void sleepTimeResponseForDifferentMaintenanceWindowParameters() throws Exception {
final DistributionSet ds = testdataFactory.createDistributionSet("");
WithSpringAuthorityRule.runAs(WithSpringAuthorityRule.withUser("tenantadmin", HAS_AUTH_TENANT_CONFIGURATION), () -> {
@@ -638,7 +637,7 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
@Test
@Description("Test download and update values before maintenance window start time.")
public void downloadAndUpdateStatusBeforeMaintenanceWindowStartTime() throws Exception {
void downloadAndUpdateStatusBeforeMaintenanceWindowStartTime() throws Exception {
Target savedTarget = testdataFactory.createTarget("1911");
final DistributionSet ds = testdataFactory.createDistributionSet("");
savedTarget = getFirstAssignedTarget(assignDistributionSetWithMaintenanceWindow(ds.getId(),
@@ -658,7 +657,7 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
@Test
@Description("Test download and update values after maintenance window start time.")
public void downloadAndUpdateStatusDuringMaintenanceWindow() throws Exception {
void downloadAndUpdateStatusDuringMaintenanceWindow() throws Exception {
Target savedTarget = testdataFactory.createTarget("1911");
final DistributionSet ds = testdataFactory.createDistributionSet("");
savedTarget = getFirstAssignedTarget(assignDistributionSetWithMaintenanceWindow(ds.getId(),
@@ -678,7 +677,7 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
@Test
@Description("Assign multiple DS in multi-assignment mode. The earliest active Action is exposed to the controller.")
public void earliestActionIsExposedToControllerInMultiAssignMode() throws Exception {
void earliestActionIsExposedToControllerInMultiAssignMode() throws Exception {
enableMultiAssignments();
final Target target = testdataFactory.createTarget();
final DistributionSet ds1 = testdataFactory.createDistributionSet(UUID.randomUUID().toString());
@@ -695,7 +694,7 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
@Test
@Description("The system should not create a new target because of a too long controller id.")
public void rootRsWithInvalidControllerId() throws Exception {
void rootRsWithInvalidControllerId() throws Exception {
final String invalidControllerId = RandomStringUtils.randomAlphabetic(Target.CONTROLLER_ID_MAX_SIZE + 1);
mvc.perform(get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), invalidControllerId))
.andExpect(status().isBadRequest());

View File

@@ -42,7 +42,7 @@ import io.qameta.allure.Story;
@ActiveProfiles({ "test" })
@Feature("Component Tests - REST Security")
@Story("Denial of Service protection filter")
public class DosFilterTest extends AbstractDDiApiIntegrationTest {
class DosFilterTest extends AbstractDDiApiIntegrationTest {
@Override
protected DefaultMockMvcBuilder createMvcWebAppContext(final WebApplicationContext context) {
@@ -52,14 +52,14 @@ public class DosFilterTest extends AbstractDDiApiIntegrationTest {
@Test
@Description("Ensures that clients that are on the blacklist are forbidded ")
public void blackListedClientIsForbidden() throws Exception {
void blackListedClientIsForbidden() throws Exception {
mvc.perform(get("/{tenant}/controller/v1/4711", tenantAware.getCurrentTenant())
.header(HttpHeaders.X_FORWARDED_FOR, "192.168.0.4 , 10.0.0.1 ")).andExpect(status().isForbidden());
}
@Test
@Description("Ensures that a READ DoS attempt is blocked ")
public void getFloddingAttackThatisPrevented() throws Exception {
void getFloddingAttackThatisPrevented() throws Exception {
MvcResult result = null;
@@ -79,7 +79,7 @@ public class DosFilterTest extends AbstractDDiApiIntegrationTest {
@Test
@Description("Ensures that an assumed READ DoS attempt is not blocked as the client (with IPv4 address) is on a whitelist")
public void unacceptableGetLoadButOnWhitelistIPv4() throws Exception {
void unacceptableGetLoadButOnWhitelistIPv4() throws Exception {
for (int i = 0; i < 100; i++) {
mvc.perform(get("/{tenant}/controller/v1/4711", tenantAware.getCurrentTenant())
.header(HttpHeaders.X_FORWARDED_FOR, "127.0.0.1")).andExpect(status().isOk());
@@ -88,7 +88,7 @@ public class DosFilterTest extends AbstractDDiApiIntegrationTest {
@Test
@Description("Ensures that an assumed READ DoS attempt is not blocked as the client (with IPv6 address) is on a whitelist")
public void unacceptableGetLoadButOnWhitelistIPv6() throws Exception {
void unacceptableGetLoadButOnWhitelistIPv6() throws Exception {
for (int i = 0; i < 100; i++) {
mvc.perform(get("/{tenant}/controller/v1/4711", tenantAware.getCurrentTenant())
.header(HttpHeaders.X_FORWARDED_FOR, "0:0:0:0:0:0:0:1")).andExpect(status().isOk());
@@ -97,7 +97,8 @@ public class DosFilterTest extends AbstractDDiApiIntegrationTest {
@Test
@Description("Ensures that a relatively high number of READ requests is allowed if it is below the DoS detection threshold")
public void acceptableGetLoad() throws Exception {
@SuppressWarnings("squid:S2925") // No idea how to get rid of the Thread.sleep here
void acceptableGetLoad() throws Exception {
for (int x = 0; x < 3; x++) {
// sleep for one second
@@ -111,7 +112,7 @@ public class DosFilterTest extends AbstractDDiApiIntegrationTest {
@Test
@Description("Ensures that a WRITE DoS attempt is blocked ")
public void putPostFloddingAttackThatisPrevented() throws Exception {
void putPostFloddingAttackThatisPrevented() throws Exception {
final Long actionId = prepareDeploymentBase();
final String feedback = JsonBuilder.deploymentActionFeedback(actionId.toString(), "proceeding");
@@ -135,7 +136,8 @@ public class DosFilterTest extends AbstractDDiApiIntegrationTest {
@Test
@Description("Ensures that a relatively high number of WRITE requests is allowed if it is below the DoS detection threshold")
public void acceptablePutPostLoad() throws Exception {
@SuppressWarnings("squid:S2925") // No idea how to get rid of the Thread.sleep here
void acceptablePutPostLoad() throws Exception {
final Long actionId = prepareDeploymentBase();
final String feedback = JsonBuilder.deploymentActionFeedback(actionId.toString(), "proceeding");