diff --git a/app/src/main/java/org/apache/roller/weblogger/business/startup/DatabaseInstaller.java b/app/src/main/java/org/apache/roller/weblogger/business/startup/DatabaseInstaller.java
index 1917c24f0..1267d4d26 100644
--- a/app/src/main/java/org/apache/roller/weblogger/business/startup/DatabaseInstaller.java
+++ b/app/src/main/java/org/apache/roller/weblogger/business/startup/DatabaseInstaller.java
@@ -226,6 +226,8 @@ public void upgradeDatabase(boolean runScripts) throws StartupException {
log.info("Database is old, beginning upgrade to version "+myVersion);
+ boolean schemaChangesRequired = dbversion < 610;
+
// iterate through each upgrade as needed
// to add to the upgrade sequence simply add a new "if" statement
// for whatever version needed and then define a new method upgradeXXX()
@@ -254,6 +256,10 @@ public void upgradeDatabase(boolean runScripts) throws StartupException {
// make sure the database version is the exact version
// we are upgrading too.
updateDatabaseVersion(con, myVersion);
+ if (!schemaChangesRequired) {
+ successMessage("No table changes were required.");
+ }
+ successMessage("Database version updated to " + myVersion + ".");
} catch (SQLException e) {
throw new StartupException("ERROR obtaining connection");
@@ -847,7 +853,8 @@ private int getDatabaseVersion() throws StartupException {
}
- private int parseVersionString(String vstring) {
+ // package-private so tests can parse versions exactly as the installer does
+ static int parseVersionString(String vstring) {
int myversion = 0;
// NOTE: this assumes a maximum of 3 digits for the version number
diff --git a/app/src/main/java/org/apache/roller/weblogger/ui/core/filters/LoadSaltFilter.java b/app/src/main/java/org/apache/roller/weblogger/ui/core/filters/LoadSaltFilter.java
index b2e63915d..8a1c830fa 100644
--- a/app/src/main/java/org/apache/roller/weblogger/ui/core/filters/LoadSaltFilter.java
+++ b/app/src/main/java/org/apache/roller/weblogger/ui/core/filters/LoadSaltFilter.java
@@ -37,6 +37,7 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha
throws IOException, ServletException {
HttpServletRequest httpReq = (HttpServletRequest) request;
+ httpReq.getSession(true);
RollerSession rollerSession = RollerSession.getRollerSession(httpReq);
if (rollerSession != null) {
String userId = rollerSession.getAuthenticatedUser() != null ? rollerSession.getAuthenticatedUser().getId() : "";
diff --git a/app/src/main/java/org/apache/roller/weblogger/ui/struts2/core/Install.java b/app/src/main/java/org/apache/roller/weblogger/ui/struts2/core/Install.java
index 355239519..feed9b0b6 100644
--- a/app/src/main/java/org/apache/roller/weblogger/ui/struts2/core/Install.java
+++ b/app/src/main/java/org/apache/roller/weblogger/ui/struts2/core/Install.java
@@ -54,6 +54,15 @@ public class Install extends UIAction {
private String databaseName = "Unknown";
+ @Override
+ public void setPageTitle(String pageTitle) {
+ this.pageTitle = pageTitle;
+ }
+
+ public String getRootCauseExceptionName() {
+ return rootCauseException == null ? "" : rootCauseException.getClass().getName();
+ }
+
@Override
public boolean isUserRequired() {
return false;
diff --git a/app/src/main/java/org/apache/roller/weblogger/ui/struts2/editor/EntryEdit.java b/app/src/main/java/org/apache/roller/weblogger/ui/struts2/editor/EntryEdit.java
index d4e0af71b..8fda6fcbd 100644
--- a/app/src/main/java/org/apache/roller/weblogger/ui/struts2/editor/EntryEdit.java
+++ b/app/src/main/java/org/apache/roller/weblogger/ui/struts2/editor/EntryEdit.java
@@ -412,7 +412,7 @@ public String getPreviewURL() {
.getUrlStrategy()
.getPreviewURLStrategy(null)
.getWeblogEntryURL(getActionWeblog(), null,
- getEntry().getAnchor(), true);
+ getEntry().getAnchor(), false);
}
/**
diff --git a/app/src/main/webapp/WEB-INF/jsps/core/DatabaseError.jsp b/app/src/main/webapp/WEB-INF/jsps/core/DatabaseError.jsp
index 9bd113353..2f1398277 100644
--- a/app/src/main/webapp/WEB-INF/jsps/core/DatabaseError.jsp
+++ b/app/src/main/webapp/WEB-INF/jsps/core/DatabaseError.jsp
@@ -32,7 +32,7 @@
- []
+ []
diff --git a/app/src/test/java/org/apache/roller/weblogger/business/startup/DatabaseInstallerUpgradeTest.java b/app/src/test/java/org/apache/roller/weblogger/business/startup/DatabaseInstallerUpgradeTest.java
new file mode 100644
index 000000000..280dde3ee
--- /dev/null
+++ b/app/src/test/java/org/apache/roller/weblogger/business/startup/DatabaseInstallerUpgradeTest.java
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+package org.apache.roller.weblogger.business.startup;
+
+import java.sql.*;
+import java.util.Properties;
+import org.apache.roller.weblogger.business.DatabaseProvider;
+import org.junit.jupiter.api.Test;
+import static org.mockito.Mockito.*;
+import static org.junit.jupiter.api.Assertions.*;
+
+class DatabaseInstallerUpgradeTest {
+ @Test
+ void versionOnlyUpgradeReportsCompletion() throws Exception {
+ DatabaseProvider db = mock(DatabaseProvider.class);
+ DatabaseScriptProvider scripts = mock(DatabaseScriptProvider.class);
+ Connection con = mock(Connection.class);
+ Statement query = mock(Statement.class);
+ ResultSet rows = mock(ResultSet.class);
+ PreparedStatement update = mock(PreparedStatement.class);
+ when(db.getConnection()).thenReturn(con);
+ when(con.createStatement()).thenReturn(query);
+ when(query.executeQuery(anyString())).thenReturn(rows);
+ when(rows.next()).thenReturn(true);
+ when(rows.getString(1)).thenReturn("610");
+ when(con.prepareStatement(anyString())).thenReturn(update);
+ DatabaseInstaller installer = new DatabaseInstaller(db, scripts);
+
+ // the version the installer writes is the one it reads from
+ // /roller-version.properties, so derive the expectation from the
+ // same source and parser instead of pinning a release constant
+ Properties props = new Properties();
+ props.load(getClass().getResourceAsStream("/roller-version.properties"));
+ int expectedVersion = DatabaseInstaller.parseVersionString(props.getProperty("ro.version", "UNKNOWN"));
+
+ installer.upgradeDatabase(true);
+ verify(update).setString(1, String.valueOf(expectedVersion));
+ verify(update).executeUpdate();
+ verifyNoInteractions(scripts);
+ assertTrue(installer.getMessages().stream().anyMatch(m -> m.contains("No table changes were required.")));
+ assertTrue(installer.getMessages().stream().anyMatch(m -> m.contains("Database version updated to " + expectedVersion + ".")));
+ }
+}
diff --git a/app/src/test/java/org/apache/roller/weblogger/ui/core/filters/LoadSaltFilterTest.java b/app/src/test/java/org/apache/roller/weblogger/ui/core/filters/LoadSaltFilterTest.java
index 5ace927a2..2087a005b 100644
--- a/app/src/test/java/org/apache/roller/weblogger/ui/core/filters/LoadSaltFilterTest.java
+++ b/app/src/test/java/org/apache/roller/weblogger/ui/core/filters/LoadSaltFilterTest.java
@@ -74,6 +74,41 @@ public void testDoFilterWithNullRollerSession() throws Exception {
}
}
+ @Test
+ void firstFormHasAUsableSalt() throws Exception {
+ javax.servlet.http.HttpSession session = mock(javax.servlet.http.HttpSession.class);
+ java.util.Map attributes = new java.util.HashMap<>();
+ java.util.Map sessionAttributes = new java.util.HashMap<>();
+ when(request.getSession(true)).thenAnswer(invocation -> {
+ when(request.getSession(false)).thenReturn(session);
+ return session;
+ });
+ when(session.getAttribute(anyString())).thenAnswer(i -> sessionAttributes.get(i.getArgument(0)));
+ doAnswer(i -> { sessionAttributes.put(i.getArgument(0), i.getArgument(1)); return null; })
+ .when(session).setAttribute(anyString(), any());
+ doAnswer(i -> { attributes.put(i.getArgument(0), i.getArgument(1)); return null; })
+ .when(request).setAttribute(anyString(), any());
+ java.util.Map salts = new java.util.HashMap<>();
+ try (MockedStatic cache = mockStatic(SaltCache.class)) {
+ cache.when(SaltCache::getInstance).thenReturn(saltCache);
+ doAnswer(i -> { salts.put(i.getArgument(0), i.getArgument(1)); return null; })
+ .when(saltCache).put(anyString(), anyString());
+ when(saltCache.get(anyString())).thenAnswer(i -> salts.get(i.getArgument(0)));
+ doAnswer(i -> { salts.remove(i.getArgument(0)); return null; })
+ .when(saltCache).remove(anyString());
+ filter.doFilter(request, response, chain);
+ String salt = (String) attributes.get("salt");
+ org.junit.jupiter.api.Assertions.assertNotNull(salt);
+ when(request.getParameter("salt")).thenReturn(null);
+ org.junit.jupiter.api.Assertions.assertFalse(SaltValidator.consumeSubmittedSalt(request));
+ when(request.getParameter("salt")).thenReturn("unknown");
+ org.junit.jupiter.api.Assertions.assertFalse(SaltValidator.consumeSubmittedSalt(request));
+ when(request.getParameter("salt")).thenReturn(salt);
+ org.junit.jupiter.api.Assertions.assertTrue(SaltValidator.consumeSubmittedSalt(request));
+ org.junit.jupiter.api.Assertions.assertFalse(SaltValidator.consumeSubmittedSalt(request));
+ }
+ }
+
private static class TestUser extends User {
private final String id;
diff --git a/app/src/test/java/org/apache/roller/weblogger/ui/struts2/core/InstallTest.java b/app/src/test/java/org/apache/roller/weblogger/ui/struts2/core/InstallTest.java
new file mode 100644
index 000000000..de2d340eb
--- /dev/null
+++ b/app/src/test/java/org/apache/roller/weblogger/ui/struts2/core/InstallTest.java
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+package org.apache.roller.weblogger.ui.struts2.core;
+
+import org.apache.roller.weblogger.business.WebloggerFactory;
+import org.apache.roller.weblogger.business.startup.*;
+import org.junit.jupiter.api.Test;
+import org.mockito.MockedStatic;
+import static org.mockito.Mockito.*;
+import static org.junit.jupiter.api.Assertions.*;
+
+class InstallTest {
+ @Test
+ void installerExposesItsTitleAndExceptionName() {
+ Install action = spy(new Install());
+ doAnswer(i -> i.getArgument(0)).when(action).getText(anyString());
+ assertEquals("", action.getRootCauseExceptionName());
+ try (MockedStatic factory = mockStatic(WebloggerFactory.class);
+ MockedStatic startup = mockStatic(WebloggerStartup.class)) {
+ startup.when(WebloggerStartup::getDatabaseProviderException)
+ .thenReturn(new StartupException("Connection failed", new IllegalStateException("offline")));
+ assertEquals("database_error", action.execute());
+ assertEquals("installer.error.connection.pageTitle", action.getPageTitle());
+ assertEquals("java.lang.IllegalStateException", action.getRootCauseExceptionName());
+ }
+ }
+}
diff --git a/app/src/test/java/org/apache/roller/weblogger/ui/struts2/editor/EntryEditPreviewTest.java b/app/src/test/java/org/apache/roller/weblogger/ui/struts2/editor/EntryEditPreviewTest.java
new file mode 100644
index 000000000..ab7389338
--- /dev/null
+++ b/app/src/test/java/org/apache/roller/weblogger/ui/struts2/editor/EntryEditPreviewTest.java
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+package org.apache.roller.weblogger.ui.struts2.editor;
+
+import java.net.URI;
+import org.apache.roller.weblogger.business.*;
+import org.apache.roller.weblogger.config.WebloggerRuntimeConfig;
+import org.apache.roller.weblogger.pojos.*;
+import org.junit.jupiter.api.Test;
+import org.mockito.MockedStatic;
+import static org.mockito.Mockito.*;
+import static org.junit.jupiter.api.Assertions.*;
+
+class EntryEditPreviewTest {
+ @Test
+ void previewUsesTheEditorsOrigin() {
+ Weblogger weblogger = mock(Weblogger.class);
+ when(weblogger.getUrlStrategy()).thenReturn(new MultiWeblogURLStrategy());
+ try (MockedStatic factory = mockStatic(WebloggerFactory.class);
+ MockedStatic config = mockStatic(WebloggerRuntimeConfig.class)) {
+ factory.when(WebloggerFactory::getWeblogger).thenReturn(weblogger);
+ config.when(WebloggerRuntimeConfig::getAbsoluteContextURL).thenReturn("http://other.example/roller");
+ Weblog weblog = new Weblog();
+ weblog.setHandle("mainpage");
+ WeblogEntry entry = new WeblogEntry();
+ entry.setAnchor("test entry");
+ EntryEdit action = new EntryEdit();
+ action.setActionWeblog(weblog);
+ action.setEntry(entry);
+ for (String context : new String[]{"", "/roller"}) {
+ config.when(WebloggerRuntimeConfig::getRelativeContextURL).thenReturn(context);
+ String preview = action.getPreviewURL();
+ assertEquals(context + "/roller-ui/authoring/preview/mainpage/?previewEntry=test+entry", preview);
+ for (String scheme : new String[]{"http", "https"}) {
+ URI editor = URI.create(scheme + "://example.org:8443" + context + "/roller-ui/authoring/entryEdit.rol");
+ URI target = editor.resolve(preview);
+ assertEquals(editor.getScheme(), target.getScheme());
+ assertEquals(editor.getAuthority(), target.getAuthority());
+ }
+ }
+ }
+ }
+}