Files
JIMRI/java/test/jmri/server/json/roster/JsonRosterSocketServiceTest.java
T
2026-06-17 14:00:51 +02:00

365 lines
19 KiB
Java

package jmri.server.json.roster;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.NullNode;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.Locale;
import javax.servlet.http.HttpServletResponse;
import jmri.InstanceManager;
import jmri.JmriException;
import jmri.jmrit.roster.Roster;
import jmri.jmrit.roster.RosterConfigManager;
import jmri.jmrit.roster.RosterEntry;
import jmri.profile.ProfileManager;
import jmri.server.json.JSON;
import jmri.server.json.JsonException;
import jmri.server.json.JsonMockConnection;
import jmri.server.json.JsonRequest;
import jmri.util.JUnitUtil;
import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
*
* @author Randall Wood Copyright 2016, 2018
*/
public class JsonRosterSocketServiceTest {
private JsonMockConnection connection = null;
private final Locale locale = Locale.ENGLISH;
@BeforeEach
public void setUp() throws Exception {
JUnitUtil.setUp();
JUnitUtil.resetProfileManager();
JUnitUtil.initConfigureManager();
connection = new JsonMockConnection((DataOutputStream) null);
JUnitUtil.initRosterConfigManager();
InstanceManager.getDefault(RosterConfigManager.class).setRoster(ProfileManager.getDefault().getActiveProfile(),
new Roster("java/test/jmri/server/json/roster/data/roster.xml"));
connection = new JsonMockConnection((DataOutputStream) null);
}
@AfterEach
public void tearDown() throws Exception {
connection = null;
JUnitUtil.tearDown();
}
/**
* Test of listen method, and of listeners.
*
* @throws java.io.IOException in event of unexpected exception
* @throws jmri.JmriException in event of unexpected exception
* @throws jmri.server.json.JsonException in event of unexpected exception
*/
@Test
public void testListen() throws IOException, JmriException, JsonException {
JsonRosterSocketService instance = new JsonRosterSocketService(this.connection);
assertEquals(0, Roster.getDefault().getPropertyChangeListeners().length);
Roster.getDefault().getEntriesInGroup(Roster.ALLENTRIES).stream().forEach((entry) -> {
assertEquals(1, entry.getPropertyChangeListeners().length);
});
// add the first time
instance.listen();
assertEquals(2, Roster.getDefault().getPropertyChangeListeners().length);
Roster.getDefault().getEntriesInGroup(Roster.ALLENTRIES).stream().forEach((entry) -> {
assertEquals(3, entry.getPropertyChangeListeners().length);
});
// don't add the second time
instance.listen();
assertEquals(2, Roster.getDefault().getPropertyChangeListeners().length);
Roster.getDefault().getEntriesInGroup(Roster.ALLENTRIES).stream().forEach((entry) -> {
assertEquals(3, entry.getPropertyChangeListeners().length);
});
// wait for the initial Bean update notifications to drain from the queue. These generate
// output messages that we then throw away.
new org.netbeans.jemmy.QueueTool().waitEmpty();
// list the groups in a JSON message for assertions
this.connection.sendMessage(null, 0);
instance.onMessage(JsonRoster.ROSTER_GROUPS, NullNode.getInstance(), new JsonRequest(locale, JSON.V5, JSON.GET, 0));
JsonNode message = this.connection.getMessage();
if (this.connection.getMessages().size() !=1) { // error, but first log why
connection.getMessages().forEach((msg)->{
log.warn(" message {}", msg);
});
}
assertEquals( 1, this.connection.getMessages().size(), "Single message sent");
assertNotNull( message, "Message was sent");
assertTrue( message.isArray(), "Message is array");
assertEquals( 2, message.size(), "Two groups exist");
assertTrue( message.findValuesAsText(JSON.NAME).contains("testGroup1"), "Contains group TestGroup1");
assertTrue( message.findValuesAsText(JSON.NAME).contains(Roster.allEntries(locale)), "Contains group AllEntries");
// add a roster group and verify message sent by listener
this.connection.sendMessage(null, 0);
Roster.getDefault().addRosterGroup("NewRosterGroup");
assertEquals( 2, this.connection.getMessages().size(), "Two replies sent");
message = this.connection.getMessage();
assertNotNull( message, "Message was sent");
assertEquals( 3, message.size(), "Three groups exist");
assertTrue( message.findValuesAsText(JSON.NAME).contains("testGroup1"), "Contains group TestGroup1");
assertTrue( message.findValuesAsText(JSON.NAME).contains(Roster.allEntries(locale)), "Contains group AllEntries");
assertTrue( message.findValuesAsText(JSON.NAME).contains("NewRosterGroup"), "Contains group NewRosterGroup");
// rename a roster group and verify message sent by listener
this.connection.sendMessage(null, 0);
var newRosterGroup = Roster.getDefault().getRosterGroups().get("NewRosterGroup");
Assertions.assertNotNull(newRosterGroup);
newRosterGroup.setName("AgedRosterGroup");
assertEquals( 1, this.connection.getMessages().size(), "Single message sent");
message = this.connection.getMessage();
assertNotNull( message, "Message was sent");
assertEquals( 3, message.size(), "Three groups exist");
assertTrue( message.findValuesAsText(JSON.NAME).contains("testGroup1"), "Contains group TestGroup1");
assertTrue( message.findValuesAsText(JSON.NAME).contains(Roster.allEntries(locale)), "Contains group AllEntries");
assertTrue( message.findValuesAsText(JSON.NAME).contains("AgedRosterGroup"), "Contains group AgedRosterGroup");
assertFalse( message.findValuesAsText(JSON.NAME).contains("NewRosterGroup"), "Contains group NewRosterGroup");
// remove a roster group and verify message sent by listener
this.connection.sendMessage(null, 0);
Roster.getDefault().removeRosterGroup(Roster.getDefault().getRosterGroups().get("AgedRosterGroup"));
assertEquals( 1, this.connection.getMessages().size(), "Single message sent");
message = this.connection.getMessage();
assertNotNull( message, "Message was sent");
assertEquals( 2, message.size(), "Two groups exist");
assertTrue( message.findValuesAsText(JSON.NAME).contains("testGroup1"), "Contains group TestGroup1");
assertTrue( message.findValuesAsText(JSON.NAME).contains(Roster.allEntries(locale)), "Contains group AllEntries");
assertFalse( message.findValuesAsText(JSON.NAME).contains("AgedRosterGroup"), "Contains group NewRosterGroup");
assertFalse( message.findValuesAsText(JSON.NAME).contains("NewRosterGroup"), "Contains group NewRosterGroup");
// Set unknown roster group directly as attribute of RosterEntry
this.connection.sendMessage(null, 0);
RosterEntry re = Roster.getDefault().getEntryForId("testEntry1");
assertEquals( 3, re.getPropertyChangeListeners().length, "instance is listening to RosterEntry");
re.putAttribute(Roster.ROSTER_GROUP_PREFIX + "attribute", "yes");
JUnitUtil.waitFor(() -> {
return this.connection.getMessages().size() >= 1;
}, "Expected message not sent");
assertEquals( 1, this.connection.getMessages().size(), "One message sent");
message = connection.getMessage();
assertNotNull( message, "Message is not null");
assertEquals( JsonRoster.ROSTER_ENTRY, message.path(JSON.TYPE).asText(),
"Message contains rosterEntry");
// Set known roster group directly as attribute of RosterEntry
Roster.getDefault().addRosterGroup("NewRosterGroup");
JUnitUtil.waitFor(() -> {
return Roster.getDefault().getRosterGroupList().contains("NewRosterGroup");
}, "Roster Group was not added");
this.connection.sendMessage(null, 0); // clear out messages
re.putAttribute(Roster.ROSTER_GROUP_PREFIX + "NewRosterGroup", "yes"); // add new group to roster entry
// wait for all expected messages to be sent before testing messages are as expected
JUnitUtil.waitFor(() -> {
return this.connection.getMessages().size() >= 3;
}, "Three expected messages not sent");
// Sent updated rosterEntry, rosterGroup, array of rosterGroup
ArrayNode messages = this.connection.getMessages();
assertEquals( 3, messages.size(), "3 messages sent");
// Check that 5 top-level types are in the 3 messages
List<String> values = messages.findValuesAsText("type");
values.sort(null); // sort because message order is non-deterministic
assertArrayEquals( new String[]{JsonRoster.ROSTER_ENTRY, JsonRoster.ROSTER_GROUP,
JsonRoster.ROSTER_GROUP, JsonRoster.ROSTER_GROUP, JsonRoster.ROSTER_GROUP},
values.toArray(new String[5]),
"Objects are 1 rosterEntry and 4 rosterGroup");
// Remove known roster group directly as attribute of RosterEntry
this.connection.sendMessage(null, 0); // clear out messages
re.deleteAttribute(Roster.ROSTER_GROUP_PREFIX + "NewRosterGroup"); // remove group from roster entry
// wait for all expected messages to be sent before testing messages are as expected
JUnitUtil.waitFor(() -> {
return this.connection.getMessages().size() >= 3;
}, "Three expected messages not sent");
// Sent updated rosterEntry, rosterGroup, array of rosterGroup
messages = this.connection.getMessages();
assertEquals( 3, messages.size(), "3 messages sent");
// Check that 5 top-level types are in the 3 messages
values = messages.findValuesAsText("type");
values.sort(null); // sort because message order is non-deterministic
assertArrayEquals( new String[]{JsonRoster.ROSTER_ENTRY, JsonRoster.ROSTER_GROUP,
JsonRoster.ROSTER_GROUP, JsonRoster.ROSTER_GROUP, JsonRoster.ROSTER_GROUP},
values.toArray(new String[5]),
"Objects are 1 rosterEntry and 4 rosterGroup");
}
/**
* Test of onMessage method DELETE on a ROSTER
*
* @throws java.io.IOException this is an error, not a failure, in the test
* @throws jmri.JmriException this is an error, not a failure, in the test
*/
@Test
public void testOnMessageDeleteRoster() throws IOException, JmriException {
JsonNode data = this.connection.getObjectMapper().createObjectNode();
JsonRosterSocketService instance = new JsonRosterSocketService(this.connection);
JsonException ex = assertThrows( JsonException.class, () ->
instance.onMessage(JsonRoster.ROSTER, data,
new JsonRequest(locale, JSON.V5, JSON.DELETE, 42)),
"Expected exception not thrown");
assertEquals(HttpServletResponse.SC_METHOD_NOT_ALLOWED, ex.getCode());
}
/**
* Test of onMessage method POST on a ROSTER
*
* @throws java.io.IOException this is an error, not a failure, in the test
* @throws jmri.JmriException this is an error, not a failure, in the test
*/
@Test
public void testOnMessagePostRoster() throws IOException, JmriException {
JsonNode data = this.connection.getObjectMapper().createObjectNode().put(JSON.METHOD, JSON.POST);
JsonRosterSocketService instance = new JsonRosterSocketService(this.connection);
JsonException ex = assertThrows( JsonException.class, () ->
instance.onMessage(JsonRoster.ROSTER, data,
new JsonRequest(locale, JSON.V5, JSON.POST, 42)),
"Expected exception not thrown");
assertEquals(HttpServletResponse.SC_NOT_IMPLEMENTED, ex.getCode());
}
/**
* Test of onMessage method PUT on a ROSTER
*
* @throws java.io.IOException this is an error, not a failure, in the test
* @throws jmri.JmriException this is an error, not a failure, in the test
*/
@Test
public void testOnMessagePutRoster() throws IOException, JmriException {
JsonNode data = this.connection.getObjectMapper().createObjectNode().put(JSON.METHOD, JSON.PUT);
JsonRosterSocketService instance = new JsonRosterSocketService(this.connection);
JsonException ex = assertThrows( JsonException.class, () ->
instance.onMessage(JsonRoster.ROSTER, data,
new JsonRequest(locale, JSON.V5, JSON.POST, 42)),
"Expected exception not thrown");
assertEquals(HttpServletResponse.SC_NOT_IMPLEMENTED, ex.getCode());
}
/**
* Test of onMessage method GET on a ROSTER
*
* @throws java.io.IOException this is an error, not a failure,
* in the test
* @throws jmri.JmriException this is an error, not a failure,
* in the test
* @throws jmri.server.json.JsonException this is an error, not a failure,
* in the test
*/
@Test
public void testOnMessageGetRoster() throws IOException, JmriException, JsonException {
JsonNode data = this.connection.getObjectMapper().createObjectNode();
JsonRosterSocketService instance = new JsonRosterSocketService(this.connection);
// assert we have not been listening
assertEquals(0, Roster.getDefault().getPropertyChangeListeners().length);
Roster.getDefault().getEntriesInGroup(Roster.ALLENTRIES).stream().forEach((entry) -> {
assertEquals(1, entry.getPropertyChangeListeners().length);
});
// onMessage should cause listening to start if it hasn't already
instance.onMessage(JsonRoster.ROSTER, data, new JsonRequest(locale, JSON.V5, JSON.GET, 0));
JsonNode message = this.connection.getMessage();
assertNotNull( message, "Message was sent");
assertEquals(Roster.getDefault().numEntries(), message.size());
// assert we are listening
assertEquals(2, Roster.getDefault().getPropertyChangeListeners().length);
Roster.getDefault().getEntriesInGroup(Roster.ALLENTRIES).stream().forEach((entry) -> {
assertEquals(3, entry.getPropertyChangeListeners().length);
});
}
/**
* Test of onMessage method INVALID on a ROSTER
*
* @throws java.io.IOException this is an error, not a failure,
* in the test
* @throws jmri.JmriException this is an error, not a failure,
* in the test
* @throws jmri.server.json.JsonException this is an error, not a failure,
* in the test
*/
@Test
public void testOnMessageInvalidRoster() throws IOException, JmriException, JsonException {
JsonNode data = this.connection.getObjectMapper().createObjectNode();
JsonRosterSocketService instance = new JsonRosterSocketService(this.connection);
JsonException ex = assertThrows( JsonException.class, () ->
instance.onMessage(JsonRoster.ROSTER, data,
new JsonRequest(locale, JSON.V5, "Invalid", 42)),
"Expected exception not thrown");
assertEquals( 405, ex.getCode(), "Exception is coded for HTTP invalid method");
assertEquals( "Method Invalid is not known and not allowed.",
ex.getLocalizedMessage(), "Exception message for HTTP invalid method");
}
/**
* Test of onList method, of class JsonRosterSocketService.
*
* @throws java.io.IOException this is an error, not a failure,
* in the test
* @throws jmri.JmriException this is an error, not a failure,
* in the test
* @throws jmri.server.json.JsonException this is an error, not a failure,
* in the test
*/
@Test
public void testOnList() throws IOException, JmriException, JsonException {
JsonNode data = this.connection.getObjectMapper().createObjectNode();
JsonRosterSocketService instance = new JsonRosterSocketService(this.connection);
// assert we have not been listening
assertEquals(0, Roster.getDefault().getPropertyChangeListeners().length);
Roster.getDefault().getEntriesInGroup(Roster.ALLENTRIES).stream().forEach((entry) -> {
assertEquals(1, entry.getPropertyChangeListeners().length);
});
// onList should cause listening to start if it hasn't already
instance.onList(JsonRoster.ROSTER, data, new JsonRequest(locale, JSON.V5, JSON.GET, 0));
JsonNode message = this.connection.getMessage();
assertNotNull(message);
assertEquals(Roster.getDefault().numEntries(), message.size());
// assert we are listening
assertEquals(2, Roster.getDefault().getPropertyChangeListeners().length);
Roster.getDefault().getEntriesInGroup(Roster.ALLENTRIES).stream().forEach((entry) -> {
assertEquals(3, entry.getPropertyChangeListeners().length);
});
}
/**
* Test of onClose method, of class JsonRosterSocketService.
*/
@Test
public void testOnClose() {
JsonRosterSocketService instance = new JsonRosterSocketService(new JsonMockConnection((DataOutputStream) null));
// listen to the roster, since onClose stops listening to the roster
instance.listen();
// assert we are listening
assertEquals(2, Roster.getDefault().getPropertyChangeListeners().length);
Roster.getDefault().getEntriesInGroup(Roster.ALLENTRIES).stream().forEach((entry) -> {
assertEquals(3, entry.getPropertyChangeListeners().length);
});
// the connection is closing, stop listening
instance.onClose();
// assert we are not listening
assertEquals(0, Roster.getDefault().getPropertyChangeListeners().length);
Roster.getDefault().getEntriesInGroup(Roster.ALLENTRIES).stream().forEach((entry) -> {
assertEquals(1, entry.getPropertyChangeListeners().length);
});
}
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(JsonRosterSocketServiceTest.class);
}