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

715 lines
32 KiB
Java

package jmri.server.json.util;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.NullNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import javax.servlet.http.HttpServletResponse;
import jmri.DccLocoAddress;
import jmri.InstanceManager;
import jmri.Metadata;
import jmri.Version;
import jmri.server.json.JsonServerPreferences;
import jmri.jmrit.display.Editor;
import jmri.jmrit.display.switchboardEditor.SwitchboardEditor;
import jmri.profile.NullProfile;
import jmri.profile.Profile;
import jmri.profile.ProfileManager;
import jmri.server.json.JSON;
import jmri.server.json.JsonException;
import jmri.server.json.JsonHttpServiceTestBase;
import jmri.server.json.JsonMockConnection;
import jmri.server.json.JsonRequest;
import jmri.util.JUnitUtil;
import jmri.util.junit.annotations.DisabledIfHeadless;
import jmri.util.node.NodeIdentity;
import jmri.util.zeroconf.ZeroConfService;
import jmri.web.server.WebServerPreferences;
import org.junit.jupiter.api.*;
import org.junit.jupiter.api.io.TempDir;
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 2018
*/
public class JsonUtilHttpServiceTest extends JsonHttpServiceTestBase<JsonUtilHttpService> {
@BeforeEach
public void setUp(@TempDir File folder) throws Exception {
super.setUp();
service = new JsonUtilHttpService(mapper);
JUnitUtil.resetWindows(true, false); // list open windows when running tests
JUnitUtil.resetNodeIdentity();
JUnitUtil.resetProfileManager(new NullProfile("JsonUtilHttpServiceTest", "12345678", folder));
JUnitUtil.initConnectionConfigManager();
JUnitUtil.initZeroConfServiceManager();
// Initialize mock PermissionManager for session authentication tests
InstanceManager.store(new MockPermissionManager(), jmri.PermissionManager.class);
}
@AfterEach
@Override
public void tearDown() throws Exception {
assertTrue(JUnitUtil.resetZeroConfServiceManager());
JUnitUtil.clearShutDownManager();
super.tearDown();
}
/**
* Test of doGet method, of class JsonUtilHttpService.
*
* @throws jmri.server.json.JsonException if test fails in an unexpected
* manner
*/
@Test
public void testDoGet() throws JsonException {
InstanceManager.getDefault(JsonServerPreferences.class).setHeartbeatInterval(10);
assertEquals(service.getHello(10, new JsonRequest(locale, JSON.V5, JSON.GET, 42)), service.doGet(JSON.HELLO, null, NullNode.getInstance(), new JsonRequest(locale, JSON.V5, JSON.GET, 42)));
assertEquals(service.getMetadata(locale, Metadata.JMRIVERCANON, 42), service.doGet(JSON.METADATA, Metadata.JMRIVERCANON, NullNode.getInstance(), new JsonRequest(locale, JSON.V5, JSON.GET, 42)));
assertEquals(service.getMetadata(new JsonRequest(locale, JSON.V5, JSON.GET, 42)), service.doGet(JSON.METADATA, null, NullNode.getInstance(), new JsonRequest(locale, JSON.V5, JSON.GET, 42)));
assertEquals(service.getNode(new JsonRequest(locale, JSON.V5, JSON.GET, 42)), service.doGet(JSON.NODE, null, NullNode.getInstance(), new JsonRequest(locale, JSON.V5, JSON.GET, 42)));
assertEquals(service.getNetworkServices(new JsonRequest(locale, JSON.V5, JSON.GET, 42)), service.doGet(JSON.NETWORK_SERVICE, null, NullNode.getInstance(), new JsonRequest(locale, JSON.V5, JSON.GET, 42)));
assertEquals(service.getNetworkServices(new JsonRequest(locale, JSON.V5, JSON.GET, 42)), service.doGet(JSON.NETWORK_SERVICES, null, NullNode.getInstance(), new JsonRequest(locale, JSON.V5, JSON.GET, 42)));
JsonException ex = assertThrows(JsonException.class, () ->
service.doGet(JSON.NETWORK_SERVICE, JSON.ZEROCONF_SERVICE_TYPE,
NullNode.getInstance(), new JsonRequest(locale, JSON.V5, JSON.GET, 42)),
"Expected exception not thrown.");
assertEquals(404, ex.getCode());
ex = assertThrows(JsonException.class, () ->
service.doGet("INVALID TYPE TOKEN", null, NullNode.getInstance(), new JsonRequest(locale, JSON.V5, JSON.GET, 42)),
"Expected exception not thrown.");
assertEquals(500, ex.getCode());
ZeroConfService zcs = ZeroConfService.create(JSON.ZEROCONF_SERVICE_TYPE, 9999);
JUnitUtil.waitFor(() -> {
return zcs.isPublished() == false;
}, "zcs.isPublished did not go false");
zcs.publish();
JUnitUtil.waitFor(() -> {
return zcs.isPublished() == true;
}, "Published ZeroConf Service");
assertEquals(service.getNetworkService(JSON.ZEROCONF_SERVICE_TYPE,
new JsonRequest (locale, JSON.V5, JSON.GET, 42)),
service.doGet(JSON.NETWORK_SERVICE, JSON.ZEROCONF_SERVICE_TYPE,
NullNode.getInstance(), new JsonRequest(locale, JSON.V5, JSON.GET, 42)));
zcs.stop();
JUnitUtil.waitFor(() -> {
return zcs.isPublished() == false;
},"zcs.isPublished did not go false after stop");
}
/**
* Test of doGetList method, of class JsonUtilHttpService. Verifies that
* JSON types that are lists are reported the same if requested using GET or
* LIST methods.
*
* @throws jmri.server.json.JsonException if test fails in an unexpected
* manner
*/
@Test
public void testDoGetList() throws JsonException {
InstanceManager.getDefault(JsonServerPreferences.class).setHeartbeatInterval(10);
assertEquals(service.getMetadata(new JsonRequest(locale, JSON.V5, JSON.GET, 42)), service.doGetList(JSON.METADATA, NullNode.getInstance(), new JsonRequest(locale, JSON.V5, JSON.GET, 42)));
assertEquals(service.getNetworkServices(new JsonRequest(locale, JSON.V5, JSON.GET, 42)), service.doGetList(JSON.NETWORK_SERVICES, NullNode.getInstance(), new JsonRequest(locale, JSON.V5, JSON.GET, 42)));
assertEquals(service.getSystemConnections(new JsonRequest(locale, JSON.V5, JSON.GET, 42)), service.doGetList(JSON.SYSTEM_CONNECTIONS, NullNode.getInstance(), new JsonRequest(locale, JSON.V5, JSON.GET, 42)));
assertEquals(service.getConfigProfiles(new JsonRequest(locale, JSON.V5, JSON.GET, 42)), service.doGetList(JSON.CONFIG_PROFILES, NullNode.getInstance(), new JsonRequest(locale, JSON.V5, JSON.GET, 42)));
}
/**
* Test of doPost method, of class JsonUtilHttpService. Verifies that POST
* and GET requests for HELLO message are the same.
*
* @throws jmri.server.json.JsonException if fails unexpectedly
*/
@Test
public void testDoPost() throws JsonException {
String type = JSON.HELLO;
String name = JSON.HELLO;
assertEquals(service.doGet(type, name, NullNode.getInstance(), new JsonRequest(locale, JSON.V5, JSON.GET, 42)), service.doPost(type, name, NullNode.getInstance(), new JsonRequest(locale, JSON.V5, JSON.GET, 42)));
}
/**
* Test of getHello method, of class JsonUtilHttpService.
*
* @throws jmri.server.json.JsonException if messages are not schema valid
*/
@Test
public void testGetHello() throws JsonException {
int heartbeat = 1000; // one second
JsonNode result = service.getHello(heartbeat, new JsonRequest(locale, JSON.V5, JSON.GET, 0));
validate(result);
assertEquals( JSON.HELLO, result.path(JSON.TYPE).asText(), "Hello type");
JsonNode data = result.path(JSON.DATA);
assertEquals( Version.name(), data.path(JSON.JMRI).asText(), "JMRI Version");
assertEquals( JSON.V5_PROTOCOL_VERSION, data.path(JSON.JSON).asText(), "JSON Complete Version");
assertEquals( JSON.V5, data.path(JSON.VERSION).asText(), "JSON Version Identifier");
assertEquals( Math.round(heartbeat * 0.9f), data.path(JSON.HEARTBEAT).asInt(), "Heartbeat");
assertEquals( InstanceManager.getDefault(WebServerPreferences.class).getRailroadName(), data.path(JSON.RAILROAD).asText(),
"RR Name");
assertEquals( NodeIdentity.networkIdentity(), data.path(JSON.NODE).asText(), "Node Identity");
Profile profile = ProfileManager.getDefault().getActiveProfile();
assertNotNull(profile);
assertEquals( profile.getName(), data.path(JSON.ACTIVE_PROFILE).asText(), "Profile");
assertEquals( 2, result.size(), "Message has 2 elements");
assertEquals( 7, data.size(), "Message data has 7 elements");
result = service.getHello(heartbeat, new JsonRequest(locale, JSON.V5, JSON.GET, 42));
validate(result);
data = result.path(JSON.DATA);
assertEquals( 3, result.size(), "Message has 2 elements");
assertEquals( 7, data.size(), "Message data has 7 elements");
}
/**
* Test of getMetadata method, of class JsonUtilHttpService.
*
* @throws jmri.server.json.JsonException if messages are not schema valid
*/
@Test
public void testGetMetadata_Locale_String() throws JsonException {
JsonNode result;
for (String metadata : Metadata.getSystemNameList()) {
result = service.getMetadata(locale, metadata, 42);
validate(result);
assertEquals(JSON.METADATA, result.path(JSON.TYPE).asText());
assertEquals(metadata, result.path(JSON.DATA).path(JSON.NAME).asText());
assertEquals(Metadata.getBySystemName(metadata), result.path(JSON.DATA).path(JSON.VALUE).asText());
}
JsonException ex = assertThrows(JsonException.class, () ->
service.getMetadata(locale, "invalid_metadata_entry", 42),
"Expected exception not thrown");
assertEquals(404, ex.getCode());
}
/**
* Test of getMetadata method, of class JsonUtilHttpService.
*
* @throws jmri.server.json.JsonException if messages are not schema valid
*/
@Test
public void testGetMetadata_Locale() throws JsonException {
JsonNode result = service.getMetadata(new JsonRequest(locale, JSON.V5, JSON.GET, 42));
validate(result);
assertEquals(Metadata.getSystemNameList().size(), result.size());
}
/**
* Test of getNetworkServices method, of class JsonUtilHttpService.
*
* @throws jmri.server.json.JsonException if messages are not schema valid
*/
@Test
public void testGetNetworkServices() throws JsonException {
// no services published
JsonNode result = service.getNetworkServices(new JsonRequest(locale, JSON.V5, JSON.GET, 42));
validate(result);
assertEquals(0, result.size());
// publish a service
ZeroConfService zcs = ZeroConfService.create(JSON.ZEROCONF_SERVICE_TYPE, 9999);
JUnitUtil.waitFor(() -> {
return zcs.isPublished() == false;
},"zcs.isPublished did not go false");
zcs.publish();
JUnitUtil.waitFor(() -> {
return zcs.isPublished() == true;
}, "Published ZeroConf Service");
result = service.getNetworkServices(new JsonRequest(locale, JSON.V5, JSON.GET, 42));
validate(result);
assertEquals(1, result.size());
assertEquals(JSON.NETWORK_SERVICE, result.get(0).path(JSON.TYPE).asText());
JsonNode data = result.get(0).path(JSON.DATA);
assertFalse(data.isMissingNode());
assertEquals(InstanceManager.getDefault(WebServerPreferences.class).getRailroadName(), data.path(JSON.USERNAME).asText());
assertEquals(9999, data.path(JSON.PORT).asInt());
assertEquals(JSON.ZEROCONF_SERVICE_TYPE, data.path(JSON.TYPE).asText());
assertEquals(NodeIdentity.networkIdentity(), data.path(JSON.NODE).asText());
assertEquals(Metadata.getBySystemName(Metadata.JMRIVERCANON), data.path("jmri").asText());
assertEquals(Metadata.getBySystemName(Metadata.JMRIVERSION), data.path("version").asText());
zcs.stop();
JUnitUtil.waitFor(() -> {
return zcs.isPublished() == false;
},"zcs.isPublished did not go false after stop");
}
/**
* Test of getNode method, of class JsonUtilHttpService.
*
* @throws jmri.server.json.JsonException if messages are not schema valid
*/
@Test
public void testGetNode() throws JsonException {
JsonNode result = service.getNode(new JsonRequest(locale, JSON.V5, JSON.GET, 42));
validate(result);
// We should have a single node with no history of nodes
// There are 3 "former" IDs when there is no history
assertEquals(JSON.NODE, result.path(JSON.TYPE).asText());
assertEquals(NodeIdentity.networkIdentity(), result.path(JSON.DATA).path(JSON.NODE).asText());
JsonNode nodes = result.path(JSON.DATA).path(JSON.FORMER_NODES);
assertTrue(nodes.isArray());
// Use whatever is returned by formerIdentities to avoid setup issues
// when running within an IDE
assertEquals(NodeIdentity.formerIdentities().size(), nodes.size());
}
/**
* Test of getSystemConnection method, of class JsonUtilHttpService.
*
* @throws jmri.server.json.JsonException if connection name not found
*/
@Test
public void testGetSystemConnection() throws JsonException {
JsonNode result = service.getSystemConnection("Internal", new JsonRequest(locale, JSON.V5, JSON.GET, 42));
validate(result);
// We should get back type, data and id
assertEquals(3, result.size());
// Data should exist and have at least 4 elements
assertTrue(result.path(JSON.DATA).size() >= 4);
assertEquals(JSON.SYSTEM_CONNECTION, result.path(JSON.TYPE).asText());
assertEquals("I", result.path(JSON.DATA).path(JSON.PREFIX).asText());
assertEquals("Internal", result.path(JSON.DATA).path(JSON.NAME).asText());
assertTrue(result.path(JSON.DATA).path(JSON.MFG).isNull());
}
/**
* Test of getSystemConnections method, of class JsonUtilHttpService.
*
* @throws jmri.server.json.JsonException if messages are not schema valid
*/
@Test
public void testGetSystemConnections() throws JsonException {
JsonNode result = service.getSystemConnections(new JsonRequest(locale, JSON.V5, JSON.GET, 42));
validate(result);
// We should only have one internal connection
assertEquals(1, result.size());
JsonNode connection = result.get(0);
assertEquals(JSON.SYSTEM_CONNECTION, connection.path(JSON.TYPE).asText());
assertEquals("I", connection.path(JSON.DATA).path(JSON.PREFIX).asText());
assertEquals("Internal", connection.path(JSON.DATA).path(JSON.NAME).asText());
assertTrue(connection.path(JSON.DATA).path(JSON.MFG).isNull());
}
/**
* Test of getNetworkService method, of class JsonUtilHttpService.
*
* @throws jmri.server.json.JsonException if test fails in an unexpected
* manner
*/
@Test
public void testGetNetworkService() throws JsonException {
JsonNode result;
// non-existent service
JsonException ex = assertThrows( JsonException.class, () ->
service.getNetworkService("non-existant-service",
new JsonRequest(locale, JSON.V5, JSON.GET, 42)),
"Expected exception not thrown ");
assertEquals(HttpServletResponse.SC_NOT_FOUND, ex.getCode());
// published service
ZeroConfService zcs = ZeroConfService.create(JSON.ZEROCONF_SERVICE_TYPE, 9999);
JUnitUtil.waitFor(() -> {
return zcs.isPublished() == false;
},"zcs.isPublished did not go false");
zcs.publish();
JUnitUtil.waitFor(() -> {
return zcs.isPublished() == true;
}, "Published ZeroConf Service");
result = service.getNetworkService(JSON.ZEROCONF_SERVICE_TYPE, new JsonRequest(locale, JSON.V5, JSON.GET, 42));
validate(result);
assertEquals(JSON.NETWORK_SERVICE, result.path(JSON.TYPE).asText());
JsonNode data = result.path(JSON.DATA);
assertFalse(data.isMissingNode());
assertEquals(InstanceManager.getDefault(WebServerPreferences.class).getRailroadName(), data.path(JSON.USERNAME).asText());
assertEquals(9999, data.path(JSON.PORT).asInt());
assertEquals(JSON.ZEROCONF_SERVICE_TYPE, data.path(JSON.TYPE).asText());
assertEquals(NodeIdentity.networkIdentity(), data.path(JSON.NODE).asText());
assertEquals(Metadata.getBySystemName(Metadata.JMRIVERCANON), data.path("jmri").asText());
assertEquals(Metadata.getBySystemName(Metadata.JMRIVERSION), data.path("version").asText());
zcs.stop();
JUnitUtil.waitFor(() -> {
return zcs.isPublished() == false;
},"zcs.isPublished did not go false after stop");
}
/**
* Test of getRailroad method, of class JsonUtilHttpService.
*
* @throws jmri.server.json.JsonException if messages are not schema valid
*/
@Test
public void testGetRailroad() throws JsonException {
JsonNode result = service.getRailroad(new JsonRequest(locale, JSON.V5, JSON.GET, 42));
validate(result);
assertEquals(JSON.RAILROAD, result.path(JSON.TYPE).asText());
JsonNode data = result.path(JSON.DATA);
assertEquals(InstanceManager.getDefault(WebServerPreferences.class).getRailroadName(),
data.path(JSON.NAME).asText());
}
/**
* Test of getPanel method, of class JsonUtilHttpService.
*
* @throws jmri.server.json.JsonException if the result cannot be validated
*/
@Test
@DisabledIfHeadless
public void testGetPanel() throws JsonException {
Editor editor = new SwitchboardEditor("test");
ObjectNode result = service.getPanel(editor, JSON.XML, 42);
validate(result);
JUnitUtil.dispose(editor.getTargetFrame());
JUnitUtil.dispose(editor);
}
/**
* Test of getPanels method, of class JsonUtilHttpService.
*
* @throws jmri.server.json.JsonException if the result cannot be validated
*/
@Test
@DisabledIfHeadless
public void testGetPanels_Locale_String() throws JsonException {
Editor editor = new SwitchboardEditor("test");
JsonNode result = service.getPanels(JSON.XML, 42);
validate(result);
JUnitUtil.dispose(editor.getTargetFrame());
JUnitUtil.dispose(editor);
}
/**
* Test of getPanels method, of class JsonUtilHttpService.
*
* @throws jmri.server.json.JsonException if the result cannot be validated
*/
@Test
@DisabledIfHeadless
public void testGetPanels_Locale() throws JsonException {
Editor editor = new SwitchboardEditor("test");
JsonNode result = service.getPanels(42);
validate(result);
JUnitUtil.dispose(editor.getTargetFrame());
JUnitUtil.dispose(editor);
}
/**
* Test of getConfigProfile method, of class JsonUtilHttpService.
* only runs negative test that a profile is not found
*
*/
@Test
public void testGetConfigProfile() {
JsonException ex = assertThrows( JsonException.class, () ->
service.getConfigProfile("non-existent-profile", new JsonRequest(locale, JSON.V5, JSON.GET, 42)),
"Expected exception not thrown");
assertEquals(HttpServletResponse.SC_NOT_FOUND, ex.getCode());
}
/**
* Test of getConfigProfiles method, of class JsonUtilHttpService. Only
* tests that result is schema valid and contains all profiles.
*
* @throws jmri.server.json.JsonException if unable to read profiles
*/
@Test
public void testGetConfigProfiles() throws JsonException {
ArrayNode result = service.getConfigProfiles(new JsonRequest(locale, JSON.V5, JSON.GET, 42));
validate(result);
assertEquals( ProfileManager.getDefault().getProfiles().length, result.size(), "Result has every profile");
}
/**
* Test of addressForString method, of class JsonUtilHttpService.
*/
@Test
public void testAddressForString() {
DccLocoAddress result = JsonUtilHttpService.addressForString("123(l)");
assertTrue( result.isLongAddress(), "Address is long");
assertEquals( 123, result.getNumber(), "Address is 123");
result = JsonUtilHttpService.addressForString("123(L)");
assertTrue( result.isLongAddress(), "Address is long");
assertEquals( 123, result.getNumber(), "Address is 123");
result = JsonUtilHttpService.addressForString("123(s)");
assertFalse( result.isLongAddress(), "Address is short");
assertEquals( 123, result.getNumber(), "Address is 123");
result = JsonUtilHttpService.addressForString("123");
assertFalse( result.isLongAddress(), "Address is short");
assertEquals( 123, result.getNumber(), "Address is 123");
result = JsonUtilHttpService.addressForString("3");
assertFalse( result.isLongAddress(), "Address is short");
assertEquals( 3, result.getNumber(), "Address is 3");
result = JsonUtilHttpService.addressForString("3(l)");
assertTrue( result.isLongAddress(), "Address is long");
assertEquals( 3, result.getNumber(), "Address is 3");
}
/**
* Test of postSessionLogin method with valid credentials.
*
* @throws jmri.server.json.JsonException if test fails unexpectedly
*/
@Test
public void testPostSessionLoginSuccess() throws JsonException {
// Create login request data
ObjectNode data = mapper.createObjectNode();
data.put("username", "testuser");
data.put("password", "testpass");
// Mock a successful login by setting up the permission system
// In actual usage, this would check against configured users
// For test purposes, we'll just verify the method handles the call
try {
JsonNode result = service.doPost(JSON.SESSION_LOGIN, "testuser", data,
new JsonRequest(locale, JSON.V5, JSON.POST, 42));
assertNotNull(result, "Result should not be null");
assertEquals(JSON.SESSION_LOGIN, result.path(JSON.TYPE).asText(), "Type should be sessionLogin");
JsonNode dataNode = result.path(JSON.DATA);
assertNotNull(dataNode, "Data node should not be null");
// Verify response structure (success field may or may not be present depending on actual login)
assertTrue(dataNode.has(JSON.USERNAME) || result.path(JSON.TYPE).asText().equals(JSON.SESSION_LOGIN),
"Response should have username or be sessionLogin type");
} catch (JsonException ex) {
// Expected for unauthorized user in test environment
assertTrue(ex.getCode() == 401 || ex.getCode() == 403,
"Should be unauthorized (401) or forbidden (403) for test user");
}
}
/**
* Test of postSessionLogin method with missing username.
*/
@Test
public void testPostSessionLoginMissingUsername() {
ObjectNode data = mapper.createObjectNode();
data.put("password", "testpass");
JsonException ex = assertThrows(JsonException.class, () -> {
service.doPost(JSON.SESSION_LOGIN, null, data,
new JsonRequest(locale, JSON.V5, JSON.POST, 42));
});
assertEquals(400, ex.getCode(), "Should be bad request (400)");
assertTrue(ex.getMessage().contains("username") || ex.getMessage().contains("parameter"),
"Error message should mention username or missing parameter");
}
/**
* Test of postSessionLogin method with guest user.
*/
@Test
public void testPostSessionLoginGuestUser() {
ObjectNode data = mapper.createObjectNode();
data.put("username", "guest");
data.put("password", "");
// Guest users should be rejected
JsonException ex = assertThrows(JsonException.class, () -> {
service.doPost(JSON.SESSION_LOGIN, "guest", data,
new JsonRequest(locale, JSON.V5, JSON.POST, 42));
});
assertEquals(403, ex.getCode(), "Should be forbidden (403) for guest user");
assertTrue(ex.getMessage().toLowerCase().contains("guest"),
"Error message should mention guest");
}
/**
* Test of postSessionLogin method with guest user (should fail).
*/
@Test
public void testPostSessionLoginInvalidCredentials() {
ObjectNode data = mapper.createObjectNode();
data.put("username", "guest");
data.put("password", "anypassword");
JsonException ex = assertThrows(JsonException.class, () -> {
service.doPost(JSON.SESSION_LOGIN, "guest", data,
new JsonRequest(locale, JSON.V5, JSON.POST, 42));
});
assertEquals(403, ex.getCode(), "Should be forbidden (403) for guest user");
}
/**
* Test of postSessionLogout method.
*
* @throws jmri.server.json.JsonException if test fails unexpectedly
*/
@Test
public void testPostSessionLogout() throws JsonException {
ObjectNode data = mapper.createObjectNode();
data.put("token", "test-token-12345");
data.put(JSON.USERNAME, "testuser");
JsonNode result = service.doPost(JSON.SESSION_LOGOUT, "testuser", data,
new JsonRequest(locale, JSON.V5, JSON.POST, 42));
assertNotNull(result, "Result should not be null");
assertEquals(JSON.SESSION_LOGOUT, result.path(JSON.TYPE).asText(), "Type should be sessionLogout");
JsonNode dataNode = result.path(JSON.DATA);
assertNotNull(dataNode, "Data node should not be null");
assertEquals("test-token-12345", dataNode.path("authenticationToken").asText(), "Should return the token");
}
/**
* Test of postSessionLogout method with empty token.
*
* @throws jmri.server.json.JsonException if test fails unexpectedly
*/
@Test
public void testPostSessionLogoutEmptyToken() throws JsonException {
ObjectNode data = mapper.createObjectNode();
data.put("token", "");
// Logout should still succeed even with empty token
JsonNode result = service.doPost(JSON.SESSION_LOGOUT, null, data,
new JsonRequest(locale, JSON.V5, JSON.POST, 42));
assertNotNull(result, "Result should not be null");
assertEquals(JSON.SESSION_LOGOUT, result.path(JSON.TYPE).asText(), "Type should be sessionLogout");
}
/**
* Test session login/logout through socket service integration.
*
* @throws java.io.IOException if test fails unexpectedly
* @throws jmri.JmriException if test fails unexpectedly
* @throws jmri.server.json.JsonException if test fails unexpectedly
*/
@Test
public void testSocketServiceSessionMessages() throws IOException, jmri.JmriException, JsonException {
JsonMockConnection connection = new JsonMockConnection((DataOutputStream) null);
JsonUtilSocketService socketService = new JsonUtilSocketService(connection);
ObjectMapper testMapper = connection.getObjectMapper();
// Test LOGIN message
ObjectNode loginData = testMapper.createObjectNode();
loginData.put("username", "testuser");
loginData.put("password", "testpass");
try {
socketService.onMessage(JSON.SESSION_LOGIN, loginData,
new JsonRequest(locale, JSON.V5, JSON.POST, 42));
JsonNode message = connection.getMessage();
// Message should either be success or error (depending on permission setup)
assertNotNull(message, "Should receive a response message");
} catch (JsonException ex) {
// Expected if no valid user configured
assertTrue(ex.getCode() == 401 || ex.getCode() == 403,
"Should be auth error for test environment");
}
// Test LOGOUT message
ObjectNode logoutData = testMapper.createObjectNode();
logoutData.put("token", "test-token");
logoutData.put(JSON.USERNAME, "testuser");
socketService.onMessage(JSON.SESSION_LOGOUT, logoutData,
new JsonRequest(locale, JSON.V5, JSON.POST, 42));
JsonNode message = connection.getMessage();
assertNotNull(message, "Should receive logout response message");
assertEquals(JSON.SESSION_LOGOUT, message.path(JSON.TYPE).asText(),
"Response type should be sessionLogout");
}
/**
* Mock PermissionManager for testing session authentication.
*/
private static class MockPermissionManager implements jmri.PermissionManager {
@Override
public boolean remoteLogin(StringBuilder sessionId, java.util.Locale locale,
String username, String password) {
// Reject guest users
if ("guest".equals(username)) {
return false;
}
// Accept any non-guest user for testing
sessionId.append("test-session-").append(username);
return true;
}
@Override
public void remoteLogout(String sessionId) {
// No-op for testing
}
@Override
public boolean isAGuestUser(String username) {
return "guest".equals(username);
}
@Override
public boolean isAGuestUser(jmri.User user) {
return user != null && isAGuestUser(user.getUserName());
}
// Stub implementations for other required methods
@Override
public jmri.Role addRole(String name) { return null; }
@Override
public void removeRole(String name) {}
@Override
public jmri.User addUser(String username, String password) { return null; }
@Override
public void removeUser(String username) {}
@Override
public void changePassword(String newPassword, String oldPassword) {}
@Override
public boolean login(String username, String password) { return false; }
@Override
public void logout() {}
@Override
public String getCurrentUserName() { return null; }
@Override
public boolean isCurrentUser(String username) { return false; }
@Override
public boolean isCurrentUser(jmri.User user) { return false; }
@Override
public boolean isCurrentUserPermittedToChangePassword() { return false; }
@Override
public boolean isLoggedIn() { return false; }
@Override
public boolean isRemotelyLoggedIn(String sessionId) { return false; }
@Override
public void addLoginListener(LoginListener listener) {}
@Override
public boolean isEnabled() { return true; }
@Override
public void setEnabled(boolean enabled) {}
@Override
public boolean isAllowEmptyPasswords() { return false; }
@Override
public void setAllowEmptyPasswords(boolean value) {}
@Override
public boolean hasAtLeastPermission(jmri.Permission permission, jmri.PermissionValue minValue) { return true; }
@Override
public boolean hasAtLeastRemotePermission(String sessionId, jmri.Permission permission, jmri.PermissionValue minValue) { return true; }
@Override
public boolean ensureAtLeastPermission(jmri.Permission permission, jmri.PermissionValue minValue) { return true; }
@Override
public void registerOwner(jmri.PermissionOwner owner) {}
@Override
public void registerPermission(jmri.Permission permission) {}
@Override
public void storePermissionSettings() {}
}
}