This is an automated email from the ASF dual-hosted git repository. Caideyipi pushed a commit to branch fix-equals-hashcode-contract-master in repository https://gitbox.apache.org/repos/asf/iotdb.git
commit dbf88e5549458f753bb5244e9f9ee0cce8cdbc2d Author: Caideyipi <[email protected]> AuthorDate: Tue Jun 9 18:54:47 2026 +0800 Fix equality hash code contracts --- .../org/apache/iotdb/pipe/api/type/Binary.java | 18 +----- .../org/apache/iotdb/pipe/api/type/BinaryTest.java | 41 +++++++++++++ .../manager/load/cache/node/NodeStatistics.java | 2 +- .../load/cache/node/NodeStatisticsTest.java | 37 ++++++++++++ .../org/apache/iotdb/metrics/utils/MetricInfo.java | 12 +--- .../apache/iotdb/metrics/utils/MetricInfoTest.java | 44 ++++++++++++++ .../org/apache/iotdb/commons/auth/entity/User.java | 8 +-- .../pipe/agent/plugin/meta/PipePluginMeta.java | 2 +- .../agent/task/meta/PipeTemporaryMetaInAgent.java | 2 +- .../iotdb/commons/trigger/TriggerInformation.java | 16 +++++- .../task/meta/PipeTemporaryMetaInAgentTest.java | 43 ++++++++++++++ .../pipe/plugin/meta/PipePluginMetaTest.java | 16 ++++++ .../commons/trigger/TriggerInformationTest.java | 67 ++++++++++++++++++++++ .../apache/iotdb/commons/utils/AuthUtilsTest.java | 53 +++++++++++++++++ 14 files changed, 324 insertions(+), 37 deletions(-) diff --git a/iotdb-api/pipe-api/src/main/java/org/apache/iotdb/pipe/api/type/Binary.java b/iotdb-api/pipe-api/src/main/java/org/apache/iotdb/pipe/api/type/Binary.java index 3a878f58a62..bb10042d66c 100644 --- a/iotdb-api/pipe-api/src/main/java/org/apache/iotdb/pipe/api/type/Binary.java +++ b/iotdb-api/pipe-api/src/main/java/org/apache/iotdb/pipe/api/type/Binary.java @@ -36,13 +36,6 @@ public class Binary implements Comparable<Binary>, Serializable { private final byte[] values; - private int hash; - - // indicate whether hash has been calculated - private boolean hasCalculatedHash; - - private String stringCache; - /** if the bytes v is modified, the modification is visible to this binary. */ public Binary(byte[] v) { this.values = v; @@ -94,11 +87,7 @@ public class Binary implements Comparable<Binary>, Serializable { @Override public int hashCode() { - if (!hasCalculatedHash) { - hash = Arrays.hashCode(values); - hasCalculatedHash = true; - } - return hash; + return Arrays.hashCode(values); } /** @@ -121,10 +110,7 @@ public class Binary implements Comparable<Binary>, Serializable { if (values == null) { return null; } - if (stringCache == null) { - stringCache = new String(this.values, STRING_CHARSET); - } - return stringCache; + return new String(this.values, STRING_CHARSET); } public String getTextEncodingType() { diff --git a/iotdb-api/pipe-api/src/test/java/org/apache/iotdb/pipe/api/type/BinaryTest.java b/iotdb-api/pipe-api/src/test/java/org/apache/iotdb/pipe/api/type/BinaryTest.java new file mode 100644 index 00000000000..ece56ca4395 --- /dev/null +++ b/iotdb-api/pipe-api/src/test/java/org/apache/iotdb/pipe/api/type/BinaryTest.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 org.apache.iotdb.pipe.api.type; + +import org.junit.Assert; +import org.junit.Test; + +public class BinaryTest { + + @Test + public void hashCodeUsesCurrentValues() { + byte[] values = new byte[] {'a'}; + Binary binary = new Binary(values); + binary.hashCode(); + binary.getStringValue(); + + values[0] = 'b'; + Binary sameBinary = new Binary(new byte[] {'b'}); + + Assert.assertEquals(binary, sameBinary); + Assert.assertEquals(binary.hashCode(), sameBinary.hashCode()); + Assert.assertEquals("b", binary.getStringValue()); + } +} diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/cache/node/NodeStatistics.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/cache/node/NodeStatistics.java index 12c554d51e8..db302387cc7 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/cache/node/NodeStatistics.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/cache/node/NodeStatistics.java @@ -81,7 +81,7 @@ public class NodeStatistics extends AbstractStatistics { @Override public int hashCode() { - return Objects.hash(status, statusReason, loadScore); + return Objects.hash(status, statusReason); } @Override diff --git a/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/manager/load/cache/node/NodeStatisticsTest.java b/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/manager/load/cache/node/NodeStatisticsTest.java new file mode 100644 index 00000000000..badc3b51531 --- /dev/null +++ b/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/manager/load/cache/node/NodeStatisticsTest.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 org.apache.iotdb.confignode.manager.load.cache.node; + +import org.apache.iotdb.commons.cluster.NodeStatus; + +import org.junit.Assert; +import org.junit.Test; + +public class NodeStatisticsTest { + + @Test + public void hashCodeMatchesEqualsWhenOnlyLoadScoreDiffers() { + NodeStatistics lowLoadStatistics = new NodeStatistics(1, NodeStatus.Running, null, 1); + NodeStatistics highLoadStatistics = new NodeStatistics(2, NodeStatus.Running, null, 2); + + Assert.assertEquals(lowLoadStatistics, highLoadStatistics); + Assert.assertEquals(lowLoadStatistics.hashCode(), highLoadStatistics.hashCode()); + } +} diff --git a/iotdb-core/metrics/interface/src/main/java/org/apache/iotdb/metrics/utils/MetricInfo.java b/iotdb-core/metrics/interface/src/main/java/org/apache/iotdb/metrics/utils/MetricInfo.java index 416a143dc6e..fff70aa2529 100644 --- a/iotdb-core/metrics/interface/src/main/java/org/apache/iotdb/metrics/utils/MetricInfo.java +++ b/iotdb-core/metrics/interface/src/main/java/org/apache/iotdb/metrics/utils/MetricInfo.java @@ -177,20 +177,12 @@ public class MetricInfo { return false; } MetaInfo that = (MetaInfo) o; - if (tagNames == null || that.tagNames == null) { - return false; - } - for (String tagName : that.tagNames) { - if (!tagNames.contains(tagName)) { - return false; - } - } - return true; + return type == that.type && Objects.equals(tagNames, that.tagNames); } @Override public int hashCode() { - return Objects.hash(tagNames); + return Objects.hash(type, tagNames); } @Override diff --git a/iotdb-core/metrics/interface/src/test/java/org/apache/iotdb/metrics/utils/MetricInfoTest.java b/iotdb-core/metrics/interface/src/test/java/org/apache/iotdb/metrics/utils/MetricInfoTest.java new file mode 100644 index 00000000000..0e42bf960b2 --- /dev/null +++ b/iotdb-core/metrics/interface/src/test/java/org/apache/iotdb/metrics/utils/MetricInfoTest.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 org.apache.iotdb.metrics.utils; + +import org.junit.Assert; +import org.junit.Test; + +public class MetricInfoTest { + + @Test + public void metaInfoEqualsHashCodeUsesTypeAndSameTagKeys() { + MetricInfo.MetaInfo metaInfo = + new MetricInfo(MetricType.COUNTER, "metric", "k1", "v1").getMetaInfo(); + MetricInfo.MetaInfo sameMetaInfo = + new MetricInfo(MetricType.COUNTER, "metric", "k1", "v2").getMetaInfo(); + MetricInfo.MetaInfo extraTagMetaInfo = + new MetricInfo(MetricType.COUNTER, "metric", "k1", "v1", "k2", "v2").getMetaInfo(); + MetricInfo.MetaInfo differentTypeMetaInfo = + new MetricInfo(MetricType.GAUGE, "metric", "k1", "v1").getMetaInfo(); + + Assert.assertEquals(metaInfo, sameMetaInfo); + Assert.assertEquals(metaInfo.hashCode(), sameMetaInfo.hashCode()); + Assert.assertNotEquals(metaInfo, extraTagMetaInfo); + Assert.assertNotEquals(extraTagMetaInfo, metaInfo); + Assert.assertNotEquals(metaInfo, differentTypeMetaInfo); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/User.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/User.java index 0bebeaf8e06..32f45e218f3 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/User.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/User.java @@ -178,13 +178,7 @@ public class User extends Role { @Override public int hashCode() { - return Objects.hash( - super.getName(), - password, - super.getPathPrivilegeList(), - super.getSysPrivilege(), - roleSet, - isOpenIdUser); + return Objects.hash(super.hashCode(), password, roleSet, isOpenIdUser); } @Override diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/agent/plugin/meta/PipePluginMeta.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/agent/plugin/meta/PipePluginMeta.java index 19f6863be3c..4029419e4e3 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/agent/plugin/meta/PipePluginMeta.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/agent/plugin/meta/PipePluginMeta.java @@ -170,7 +170,7 @@ public class PipePluginMeta { @Override public int hashCode() { - return pluginName.hashCode(); + return Objects.hash(pluginName, className, isBuiltin, jarName, jarMD5); } @Override diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/agent/task/meta/PipeTemporaryMetaInAgent.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/agent/task/meta/PipeTemporaryMetaInAgent.java index 23914fa8d84..5527622ca1b 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/agent/task/meta/PipeTemporaryMetaInAgent.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/agent/task/meta/PipeTemporaryMetaInAgent.java @@ -92,7 +92,7 @@ public class PipeTemporaryMetaInAgent implements PipeTemporaryMeta { @Override public int hashCode() { - return Objects.hash(floatingMemoryUsageInByte, regionId2CommitterKeyMap); + return Objects.hash(floatingMemoryUsageInByte.get(), regionId2CommitterKeyMap); } @Override diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/trigger/TriggerInformation.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/trigger/TriggerInformation.java index c25d185b0cd..5f2eea66dc5 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/trigger/TriggerInformation.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/trigger/TriggerInformation.java @@ -157,6 +157,7 @@ public class TriggerInformation { TriggerInformation that = (TriggerInformation) o; return Objects.equals(triggerName, that.triggerName) && Objects.equals(pathPattern, that.pathPattern) + && isUsingURI == that.isUsingURI && isStateful == that.isStateful && Objects.equals(className, that.className) && Objects.equals(jarName, that.jarName) @@ -164,12 +165,25 @@ public class TriggerInformation { && event == that.event && triggerState == that.triggerState && (!isStateful() || Objects.equals(dataNodeLocation, that.dataNodeLocation)) + && failureStrategy == that.failureStrategy && Objects.equals(jarFileMD5, that.jarFileMD5); } @Override public int hashCode() { - return Objects.hash(triggerName); + return Objects.hash( + triggerName, + pathPattern, + isUsingURI, + isStateful, + className, + jarName, + attributes, + event, + triggerState, + isStateful ? dataNodeLocation : null, + failureStrategy, + jarFileMD5); } public PartialPath getPathPattern() { diff --git a/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/pipe/agent/task/meta/PipeTemporaryMetaInAgentTest.java b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/pipe/agent/task/meta/PipeTemporaryMetaInAgentTest.java new file mode 100644 index 00000000000..065a5a1dafb --- /dev/null +++ b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/pipe/agent/task/meta/PipeTemporaryMetaInAgentTest.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 org.apache.iotdb.commons.pipe.agent.task.meta; + +import org.junit.Assert; +import org.junit.Test; + +public class PipeTemporaryMetaInAgentTest { + + @Test + public void equalsHashCodeUsesFloatingMemoryValue() { + PipeTemporaryMetaInAgent meta = new PipeTemporaryMetaInAgent("pipe", 1); + meta.addFloatingMemoryUsageInByte(100); + meta.getCommitterKey("pipe", 1, 1, 0); + + PipeTemporaryMetaInAgent sameMeta = new PipeTemporaryMetaInAgent("pipe", 1); + sameMeta.addFloatingMemoryUsageInByte(100); + sameMeta.getCommitterKey("pipe", 1, 1, 0); + + Assert.assertEquals(meta, sameMeta); + Assert.assertEquals(meta.hashCode(), sameMeta.hashCode()); + + sameMeta.decreaseFloatingMemoryUsageInByte(1); + Assert.assertNotEquals(meta, sameMeta); + } +} diff --git a/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/pipe/plugin/meta/PipePluginMetaTest.java b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/pipe/plugin/meta/PipePluginMetaTest.java index b5b854adc4d..da9d493aecb 100644 --- a/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/pipe/plugin/meta/PipePluginMetaTest.java +++ b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/pipe/plugin/meta/PipePluginMetaTest.java @@ -22,6 +22,7 @@ package org.apache.iotdb.commons.pipe.plugin.meta; import org.apache.iotdb.commons.pipe.agent.plugin.builtin.BuiltinPipePlugin; import org.apache.iotdb.commons.pipe.agent.plugin.meta.ConfigNodePipePluginMetaKeeper; import org.apache.iotdb.commons.pipe.agent.plugin.meta.DataNodePipePluginMetaKeeper; +import org.apache.iotdb.commons.pipe.agent.plugin.meta.PipePluginMeta; import org.junit.Assert; import org.junit.Test; @@ -32,6 +33,21 @@ import java.io.IOException; public class PipePluginMetaTest { + @Test + public void testPipePluginMetaHashCodeIncludesAllFields() { + PipePluginMeta pipePluginMeta = + new PipePluginMeta("test", "org.apache.iotdb.Test", false, "test.jar", "md5"); + PipePluginMeta samePipePluginMeta = + new PipePluginMeta("TEST", "org.apache.iotdb.Test", false, "test.jar", "md5"); + PipePluginMeta differentClassNamePipePluginMeta = + new PipePluginMeta("test", "org.apache.iotdb.AnotherTest", false, "test.jar", "md5"); + + Assert.assertEquals(pipePluginMeta, samePipePluginMeta); + Assert.assertEquals(pipePluginMeta.hashCode(), samePipePluginMeta.hashCode()); + Assert.assertNotEquals(pipePluginMeta, differentClassNamePipePluginMeta); + Assert.assertNotEquals(pipePluginMeta.hashCode(), differentClassNamePipePluginMeta.hashCode()); + } + @Test public void testConfigNodePipePluginMetaKeeper() { ConfigNodePipePluginMetaKeeper keeper = new ConfigNodePipePluginMetaKeeper(); diff --git a/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/trigger/TriggerInformationTest.java b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/trigger/TriggerInformationTest.java new file mode 100644 index 00000000000..e6dd037fac9 --- /dev/null +++ b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/trigger/TriggerInformationTest.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 org.apache.iotdb.commons.trigger; + +import org.apache.iotdb.commons.exception.IllegalPathException; +import org.apache.iotdb.commons.path.PartialPath; +import org.apache.iotdb.confignode.rpc.thrift.TTriggerState; +import org.apache.iotdb.trigger.api.enums.FailureStrategy; +import org.apache.iotdb.trigger.api.enums.TriggerEvent; + +import org.junit.Assert; +import org.junit.Test; + +import java.util.Collections; + +public class TriggerInformationTest { + + @Test + public void equalsHashCodeIncludesUriUsageAndFailureStrategy() throws IllegalPathException { + TriggerInformation triggerInformation = + createTriggerInformation(true, FailureStrategy.OPTIMISTIC); + TriggerInformation sameTriggerInformation = + createTriggerInformation(true, FailureStrategy.OPTIMISTIC); + + Assert.assertEquals(triggerInformation, sameTriggerInformation); + Assert.assertEquals(triggerInformation.hashCode(), sameTriggerInformation.hashCode()); + + Assert.assertNotEquals( + triggerInformation, createTriggerInformation(true, FailureStrategy.PESSIMISTIC)); + Assert.assertNotEquals( + triggerInformation, createTriggerInformation(false, FailureStrategy.OPTIMISTIC)); + } + + private TriggerInformation createTriggerInformation( + boolean isUsingURI, FailureStrategy failureStrategy) throws IllegalPathException { + return new TriggerInformation( + new PartialPath("root.test.**"), + "trigger", + "trigger.class", + isUsingURI, + "trigger.jar", + Collections.singletonMap("k", "v"), + TriggerEvent.AFTER_INSERT, + TTriggerState.INACTIVE, + false, + null, + failureStrategy, + "jar-md5"); + } +} diff --git a/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/utils/AuthUtilsTest.java b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/utils/AuthUtilsTest.java index 7809d005e98..6500d8ab1db 100644 --- a/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/utils/AuthUtilsTest.java +++ b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/utils/AuthUtilsTest.java @@ -22,6 +22,7 @@ package org.apache.iotdb.commons.utils; import org.apache.iotdb.commons.auth.AuthException; import org.apache.iotdb.commons.auth.entity.PathPrivilege; import org.apache.iotdb.commons.auth.entity.PrivilegeType; +import org.apache.iotdb.commons.auth.entity.Role; import org.apache.iotdb.commons.auth.entity.User; import org.apache.iotdb.commons.conf.IoTDBConstant; import org.apache.iotdb.commons.exception.IllegalPathException; @@ -136,6 +137,58 @@ public class AuthUtilsTest { AuthUtils.getPrivileges(path, privilegeList).contains(PrivilegeType.WRITE_DATA)); } + @Test + public void authUtilsTest_PathPrivilegeEqualsIncludesGrantOption() throws IllegalPathException { + PathPrivilege withGrantOption = new PathPrivilege(new PartialPath("root.t1")); + withGrantOption.grantPrivilege(PrivilegeType.WRITE_SCHEMA, true); + + PathPrivilege withoutGrantOption = new PathPrivilege(new PartialPath("root.t1")); + withoutGrantOption.grantPrivilege(PrivilegeType.WRITE_SCHEMA, false); + + Assert.assertFalse(withGrantOption.equals(withoutGrantOption)); + + withoutGrantOption.grantPrivilege(PrivilegeType.WRITE_SCHEMA, true); + Assert.assertEquals(withGrantOption, withoutGrantOption); + Assert.assertEquals(withGrantOption.hashCode(), withoutGrantOption.hashCode()); + } + + @Test + public void authUtilsTest_RoleEqualsHashCodeIncludesSysGrantOption() { + Role withGrantOption = new Role("role"); + withGrantOption.grantSysPrivilege(PrivilegeType.MANAGE_USER, true); + + Role withoutGrantOption = new Role("role"); + withoutGrantOption.grantSysPrivilege(PrivilegeType.MANAGE_USER, false); + + Assert.assertFalse(withGrantOption.equals(withoutGrantOption)); + Assert.assertNotEquals(withGrantOption.hashCode(), withoutGrantOption.hashCode()); + + withoutGrantOption.getSysPriGrantOpt().add(PrivilegeType.MANAGE_USER); + Assert.assertEquals(withGrantOption, withoutGrantOption); + Assert.assertEquals(withGrantOption.hashCode(), withoutGrantOption.hashCode()); + } + + @Test + public void authUtilsTest_UserEqualsHashCodeIncludesUserState() { + User user = new User("user", "password"); + user.grantSysPrivilege(PrivilegeType.MANAGE_USER, true); + user.setOpenIdUser(true); + + User userWithoutGrantOption = new User("user", "password"); + userWithoutGrantOption.grantSysPrivilege(PrivilegeType.MANAGE_USER, false); + userWithoutGrantOption.setOpenIdUser(true); + + Assert.assertFalse(user.equals(userWithoutGrantOption)); + Assert.assertNotEquals(user.hashCode(), userWithoutGrantOption.hashCode()); + + userWithoutGrantOption.getSysPriGrantOpt().add(PrivilegeType.MANAGE_USER); + Assert.assertEquals(user, userWithoutGrantOption); + Assert.assertEquals(user.hashCode(), userWithoutGrantOption.hashCode()); + + userWithoutGrantOption.setOpenIdUser(false); + Assert.assertFalse(user.equals(userWithoutGrantOption)); + } + @Test public void authUtilsTest_PathPrivilegeAddRemove() throws IllegalPathException, AuthException { List<PathPrivilege> privs = new ArrayList<>();
