AddServletTest.java
/*******************************************************************************
* Copyright (c) 2025 Eclipse RDF4J contributors.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Distribution License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*******************************************************************************/
// Some portions generated by Codex
package org.eclipse.rdf4j.workbench.commands;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatCode;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.StringReader;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
import javax.servlet.http.HttpServletResponse;
import javax.xml.transform.Templates;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.eclipse.rdf4j.repository.Repository;
import org.eclipse.rdf4j.repository.RepositoryConnection;
import org.eclipse.rdf4j.workbench.util.WorkbenchRequest;
import org.junit.jupiter.api.Test;
class AddServletTest {
private static final Path ADD_XSL = Paths.get("src", "main", "webapp", "transformations", "add.xsl");
@Test
void addPageRendersIsolationOptionsFromResults() throws Exception {
TransformerFactory factory = TransformerFactory.newInstance();
StreamSource stylesheet = new StreamSource(ADD_XSL.toFile());
stylesheet.setSystemId(ADD_XSL.toUri().toString());
Templates templates = factory.newTemplates(stylesheet);
Transformer transformer = templates.newTransformer();
String sparqlResults = ""
+ "<?xml version=\"1.0\"?>\n"
+ "<sparql xmlns=\"http://www.w3.org/2005/sparql-results#\">\n"
+ " <head />\n"
+ " <results>\n"
+ " <result>\n"
+ " <binding name=\"isolation-level-option\">\n"
+ " <literal>NONE</literal>\n"
+ " </binding>\n"
+ " <binding name=\"isolation-level-option-label\">\n"
+ " <literal>None</literal>\n"
+ " </binding>\n"
+ " </result>\n"
+ " <result>\n"
+ " <binding name=\"isolation-level-option\">\n"
+ " <literal>READ_COMMITTED</literal>\n"
+ " </binding>\n"
+ " <binding name=\"isolation-level-option-label\">\n"
+ " <literal>Read Committed</literal>\n"
+ " </binding>\n"
+ " </result>\n"
+ " </results>\n"
+ "</sparql>\n";
StringWriter html = new StringWriter();
transformer.transform(new StreamSource(new StringReader(sparqlResults)), new StreamResult(html));
String output = html.toString();
assertThat(output).contains("value=\"NONE\"")
.contains(">None<")
.contains("value=\"READ_COMMITTED\"")
.contains(">Read Committed<")
.doesNotContain("value=\"SNAPSHOT\"");
}
@Test
void addPageUsesTransactionSettingParam() throws Exception {
TransformerFactory factory = TransformerFactory.newInstance();
StreamSource stylesheet = new StreamSource(ADD_XSL.toFile());
stylesheet.setSystemId(ADD_XSL.toUri().toString());
Templates templates = factory.newTemplates(stylesheet);
Transformer transformer = templates.newTransformer();
String sparqlResults = ""
+ "<?xml version=\"1.0\"?>\n"
+ "<sparql xmlns=\"http://www.w3.org/2005/sparql-results#\">\n"
+ " <head />\n"
+ " <results />\n"
+ "</sparql>\n";
StringWriter html = new StringWriter();
transformer.transform(new StreamSource(new StringReader(sparqlResults)), new StreamResult(html));
String output = html.toString();
assertThat(output)
.contains("name=\"transaction-setting__org.eclipse.rdf4j.common.transaction.IsolationLevel\"");
}
@Test
void doPostReadsTransactionSettingParameter() throws Exception {
AddServlet servlet = new AddServlet();
Repository repository = mock(Repository.class);
RepositoryConnection connection = mock(RepositoryConnection.class);
when(repository.getConnection()).thenReturn(connection);
when(connection.isActive()).thenReturn(true);
servlet.setRepository(repository);
WorkbenchRequest request = mock(WorkbenchRequest.class);
when(request.getParameter("Content-Type")).thenReturn("text/turtle");
when(request.getParameter("baseURI")).thenReturn("http://example/base");
when(request.isParameterPresent("context")).thenReturn(false);
when(request.isParameterPresent("url")).thenReturn(false);
when(request.getContentParameter()).thenReturn(
new ByteArrayInputStream("<a> <b> <c> .".getBytes(StandardCharsets.UTF_8)));
when(request.getContentFileName()).thenReturn("data.ttl");
when(request.getParameter("transaction-setting__org.eclipse.rdf4j.common.transaction.IsolationLevel"))
.thenReturn("READ_COMMITTED");
HttpServletResponse response = mock(HttpServletResponse.class);
when(response.getOutputStream()).thenReturn(mock(ServletOutputStream.class));
servlet.doPost(request, response, "");
verify(connection).commit();
verify(request).getParameter("transaction-setting__org.eclipse.rdf4j.common.transaction.IsolationLevel");
}
@Test
void serviceUsesTwoColumnsForIsolationLevelOptions() throws Exception {
AddServlet servlet = new TestAddServlet();
WorkbenchRequest request = mock(WorkbenchRequest.class);
when(request.getParameter("transaction-setting__org.eclipse.rdf4j.common.transaction.IsolationLevel"))
.thenReturn("READ_COMMITTED");
HttpServletResponse response = mock(HttpServletResponse.class);
when(response.getOutputStream()).thenReturn(mock(ServletOutputStream.class));
servlet.service(request, response, "");
}
@Test
void doPostIncludesIsolationLevelBindingInErrorResponse() throws Exception {
AddServlet servlet = new AddServlet();
WorkbenchRequest request = mock(WorkbenchRequest.class);
when(request.getParameter("baseURI")).thenReturn("http://example/base");
when(request.getParameter("Content-Type")).thenReturn(null);
when(request.isParameterPresent("context")).thenReturn(false);
when(request.isParameterPresent("url")).thenReturn(false);
when(request.getContentParameter()).thenReturn(new ByteArrayInputStream(new byte[0]));
when(request.getContentFileName()).thenReturn("data.ttl");
when(request.getParameter("transaction-setting__org.eclipse.rdf4j.common.transaction.IsolationLevel"))
.thenReturn("READ_COMMITTED");
HttpServletResponse response = mock(HttpServletResponse.class);
RecordingServletOutputStream outputStream = new RecordingServletOutputStream();
when(response.getOutputStream()).thenReturn(outputStream);
assertThatCode(() -> servlet.doPost(request, response, "transformations")).doesNotThrowAnyException();
assertThat(outputStream.asString())
.contains("<binding name='transaction-setting__org.eclipse.rdf4j.common.transaction.IsolationLevel'>")
.contains(">READ_COMMITTED<");
}
@Test
void doPostErrorIncludesIsolationLevelOptions() throws Exception {
AddServlet servlet = new RecordingAddServlet();
WorkbenchRequest request = mock(WorkbenchRequest.class);
when(request.getParameter("baseURI")).thenReturn("http://example/base");
when(request.getParameter("Content-Type")).thenReturn(null);
when(request.isParameterPresent("context")).thenReturn(false);
when(request.isParameterPresent("url")).thenReturn(false);
when(request.getContentParameter()).thenReturn(new ByteArrayInputStream(new byte[0]));
when(request.getContentFileName()).thenReturn("data.ttl");
when(request.getParameter("transaction-setting__org.eclipse.rdf4j.common.transaction.IsolationLevel"))
.thenReturn("SNAPSHOT");
HttpServletResponse response = mock(HttpServletResponse.class);
RecordingServletOutputStream outputStream = new RecordingServletOutputStream();
when(response.getOutputStream()).thenReturn(outputStream);
assertThatCode(() -> servlet.doPost(request, response, "transformations")).doesNotThrowAnyException();
String output = outputStream.asString();
assertThat(output)
.contains("<binding name='isolation-level-option'>")
.contains("<binding name='isolation-level-option-label'>")
.contains(">READ_COMMITTED<")
.contains(">SNAPSHOT<")
.contains(">Read Committed<")
.contains(">Snapshot<");
}
@Test
void serviceEmitsSelectedIsolationLevelBinding() throws Exception {
AddServlet servlet = new RecordingAddServlet();
WorkbenchRequest request = mock(WorkbenchRequest.class);
when(request.getParameter("transaction-setting__org.eclipse.rdf4j.common.transaction.IsolationLevel"))
.thenReturn("SNAPSHOT");
HttpServletResponse response = mock(HttpServletResponse.class);
RecordingServletOutputStream outputStream = new RecordingServletOutputStream();
when(response.getOutputStream()).thenReturn(outputStream);
servlet.service(request, response, "transformations");
assertThat(outputStream.asString())
.contains("<binding name='transaction-setting__org.eclipse.rdf4j.common.transaction.IsolationLevel'>")
.contains(">SNAPSHOT<");
}
private static class TestAddServlet extends AddServlet {
@Override
List<String> determineIsolationLevels() {
return List.of("READ_COMMITTED");
}
}
private static class RecordingAddServlet extends AddServlet {
@Override
List<String> determineIsolationLevels() {
return List.of("READ_COMMITTED", "SNAPSHOT");
}
}
private static class RecordingServletOutputStream extends ServletOutputStream {
private final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
@Override
public void write(int b) {
buffer.write(b);
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setWriteListener(WriteListener writeListener) {
// no-op
}
String asString() {
return buffer.toString(StandardCharsets.UTF_8);
}
}
}