Skip to content

Commit 9d57085

Browse files
committed
Separate stages of server preparation for better diagnosis of problems
1 parent b23612c commit 9d57085

File tree

4 files changed

+183
-31
lines changed

4 files changed

+183
-31
lines changed

src/main/java/org/solid/testharness/config/TestSubject.java

Lines changed: 58 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,10 @@
3838
import org.solid.common.vocab.EARL;
3939
import org.solid.common.vocab.PIM;
4040
import org.solid.common.vocab.RDF;
41+
import org.solid.testharness.api.TestHarnessApiException;
4142
import org.solid.testharness.http.ClientRegistry;
4243
import org.solid.testharness.http.HttpConstants;
44+
import org.solid.testharness.http.HttpUtils;
4345
import org.solid.testharness.http.SolidClientProvider;
4446
import org.solid.testharness.utils.DataRepository;
4547
import org.solid.testharness.utils.SolidContainerProvider;
@@ -51,10 +53,10 @@
5153
import java.io.IOException;
5254
import java.io.InputStream;
5355
import java.net.URI;
56+
import java.net.http.HttpResponse;
5457
import java.text.MessageFormat;
5558
import java.util.List;
5659
import java.util.Set;
57-
import java.util.stream.Collectors;
5860

5961
import static org.eclipse.rdf4j.model.util.Values.iri;
6062

@@ -154,6 +156,25 @@ public void prepareServer() {
154156
testRunContainer = rootTestContainer.reserveContainer(rootTestContainer.generateId()).instantiate();
155157
logger.debug("Test run container content: {}", testRunContainer.getContentAsTurtle());
156158
logger.debug("Test run container access controls: {}", testRunContainer.getAccessDataset());
159+
160+
// check that we can change the access control of a resource so we know those tests can run
161+
final var aclTestContainer = testRunContainer.reserveContainer("acltest");
162+
try {
163+
aclTestContainer .instantiate();
164+
final var builder = aclTestContainer.getAccessDatasetBuilder();
165+
final var bobReadAcl = builder
166+
.setAgentAccess(
167+
aclTestContainer.getUrl().toString(),
168+
config.getWebIds().get(HttpConstants.BOB),
169+
List.of("read")
170+
).build();
171+
aclTestContainer.setAccessDataset(bobReadAcl); // MOCK THIS PASS/FAIL
172+
logger.info("Confirmed we can create a container [{}] and set ACLs on it", aclTestContainer.getUrl());
173+
} catch (TestHarnessException | TestHarnessApiException ex) {
174+
logger.warn("Failed to create a container [{}] and set ACLs on it: {}",
175+
aclTestContainer.getUrl(), ex.getMessage());
176+
throw ex;
177+
}
157178
} catch (TestHarnessException | RuntimeException e) {
158179
throw new TestHarnessInitializationException("Failed to prepare server", e);
159180
}
@@ -168,6 +189,7 @@ private void determineAccessControlImplementation(final SolidClientProvider owne
168189
rootTestContainer.getUrl());
169190
}
170191
accessControlMode = ownerClient.getAclType(aclUrl);
192+
logger.info("The Pod is using [{}] for access control", accessControlMode);
171193
}
172194

173195
URI findTestContainer() throws TestHarnessException {
@@ -198,37 +220,63 @@ URI findStorage() throws TestHarnessException {
198220
final Model profile;
199221
try {
200222
profile = publicClient.getContentAsModel(webId);
223+
logger.info("Loaded WebID Document for [{}]", webId);
201224
} catch (Exception e) {
202225
throw new TestHarnessInitializationException(MessageFormat.format(
203-
"Failed to read WebID Profile Document for [{0}]", webId), e);
226+
"Failed to read WebID Document for [{0}]", webId), e);
204227
}
205-
final List<URI> pods = profile.filter(iri(webId.toString()), PIM.storage, null)
228+
229+
final var storages = profile.filter(iri(webId.toString()), PIM.storage, null)
206230
.objects()
207231
.stream()
208232
.filter(Value::isIRI)
209233
.map(Value::stringValue)
210234
.map(URI::create)
235+
.toList();
236+
if (storages.isEmpty()) {
237+
logger.warn("No Pod references found in the WebID Document for [{}], looking for predicate [{}]",
238+
webId, PIM.storage);
239+
throw new TestHarnessInitializationException(MessageFormat.format(
240+
"No Pod references found in the WebID Document for [{0}], looking for predicate [{1}]",
241+
webId, PIM.storage));
242+
}
243+
logger.info("Found [{}] Pods, checking them...", storages.size());
244+
final List<URI> pods = storages.stream()
211245
.filter(p -> isPodAccessible(p, ownerClient))
212-
.collect(Collectors.toList());
246+
.toList();
247+
213248
if (pods.isEmpty()) {
214249
throw new TestHarnessInitializationException(MessageFormat.format(
215-
"Pod provisioning is not yet implemented. " +
216-
"Please ensure the storage already exists for the test user: [{0}] " +
217-
"and that pim:storage is defined in the WebID Profile Document.", webId));
250+
"No accessible Pods were found for test user: [{0}]. " +
251+
"Please check the logs to see why, then ensure that Pod storage exists " +
252+
"and that pim:storage is defined in their WebID Document.", webId));
218253
// return provisionPod();
219254
} else {
220-
logger.info("Storage found via WebID Profile Document: {}", pods.get(0));
255+
logger.info("Pod found: [{}]", pods.get(0));
221256
return pods.get(0);
222257
}
223258
}
224259

225260
boolean isPodAccessible(final URI pod, final SolidClientProvider ownerClient) {
261+
logger.info("Checking Pod [{}]", pod);
226262
try {
227-
return ownerClient.hasStorageType(pod);
263+
// is it present?
264+
final HttpResponse<Void> response = ownerClient.getClient().head(pod);
265+
if (!HttpUtils.isSuccessful(response.statusCode())) {
266+
logger.warn("HEAD request to Pod [{}] returned [{}]", pod, response.statusCode());
267+
return false;
268+
}
269+
// is it a storage?
270+
final var storageLink = HttpUtils.getHeaderLinkByType(response.headers(), PIM.Storage.toString());
271+
if (storageLink == null) {
272+
logger.warn("Pod [{}] is not a valid storage as it missing a [{}] header", pod, PIM.Storage);
273+
return false;
274+
}
228275
} catch (Exception e) {
229-
logger.warn("Failed to check pod accessibility: {}", pod);
276+
logger.warn("Failed to check pod [{}] accessibility due to: [{}]", pod, e.getMessage());
230277
return false;
231278
}
279+
return true;
232280
}
233281

234282
/*

src/main/java/org/solid/testharness/http/HttpUtils.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ public static HttpRequest.BodyPublisher ofFormData(@NotNull final Map<Object, Ob
176176
Objects.requireNonNull(data, "data is required");
177177
final var builder = new StringBuilder();
178178
for (Map.Entry<Object, Object> entry : data.entrySet()) {
179-
if (builder.length() > 0) {
179+
if (!builder.isEmpty()) {
180180
builder.append('&');
181181
}
182182
builder.append(encodeValue(entry.getKey().toString()));
@@ -232,11 +232,20 @@ public static List<Link> parseLinkHeaders(@NotNull final HttpHeaders headers) {
232232
List<String> links = headers.allValues(HttpConstants.HEADER_LINK);
233233
// TODO: the following must be applied to all link headers, then the whole list flattened
234234
if (links.size() == 1 && links.get(0).contains(",")) {
235-
links = Arrays.stream(links.get(0).split(",")).map(String::strip).collect(Collectors.toList());
235+
links = Arrays.stream(links.get(0).split(",")).map(String::strip).toList();
236236
}
237237
return links.stream().map(Link::valueOf).collect(Collectors.toList());
238238
}
239239

240+
public static URI getHeaderLinkByType(@NotNull final HttpHeaders headers, @NotNull final String type) {
241+
final List<Link> links = parseLinkHeaders(headers);
242+
return links.stream()
243+
.filter(link -> link.getRels().contains("type") &&
244+
type.equals(link.getUri().toString()))
245+
.findFirst()
246+
.map(Link::getUri).orElse(null);
247+
}
248+
240249
private static Config getConfig() {
241250
synchronized (HttpUtils.class) {
242251
if (config == null) {

0 commit comments

Comments
 (0)