Skip to content

Commit a9d1191

Browse files
timea-solidacoburn
andauthored
JCL-422: Performance testing support (#591)
Co-authored-by: Aaron Coburn <aaronc@inrupt.com>
1 parent 82fc9d6 commit a9d1191

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+2658
-8
lines changed

.github/workflows/ci-config.yml

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,13 @@ jobs:
2323
cache: 'maven'
2424

2525
- name: Build the code with Maven
26-
run: mvn -B -ntp install -Pci -pl -integration/uma,-integration/openid
26+
run: mvn -B -ntp install -Pci -pl -integration/uma,-integration/openid,-performance/uma
2727

2828
- name: Prod Integration tests
2929
if: ${{ github.actor != 'dependabot[bot]' }}
3030
run: mvn -B -ntp verify -Pci -pl integration/uma,integration/openid
3131
env:
3232
INRUPT_TEST_WEBID: ${{ secrets.INRUPT_PROD_WEBID }}
33-
INRUPT_TEST_PUBLIC_RESOURCE_PATH: ${{ secrets.INRUPT_PROD_PUBLIC_RESOURCE_PATH }}
3433
INRUPT_TEST_ACCESS_GRANT_PROVIDER: ${{ secrets.INRUPT_PROD_ACCESS_GRANT_PROVIDER }}
3534
INRUPT_TEST_CLIENT_ID: ${{ secrets.INRUPT_PROD_CLIENT_ID }}
3635
INRUPT_TEST_CLIENT_SECRET: ${{ secrets.INRUPT_PROD_CLIENT_SECRET }}
@@ -44,14 +43,34 @@ jobs:
4443
run: mvn -B -ntp verify -Pci -pl integration/uma,integration/openid
4544
env:
4645
INRUPT_TEST_WEBID: ${{ secrets.INRUPT_DEV_WEBID }}
47-
INRUPT_TEST_PUBLIC_RESOURCE_PATH: ${{ secrets.INRUPT_DEV_PUBLIC_RESOURCE_PATH }}
4846
INRUPT_TEST_ACCESS_GRANT_PROVIDER: ${{ secrets.INRUPT_DEV_ACCESS_GRANT_PROVIDER }}
4947
INRUPT_TEST_CLIENT_ID: ${{ secrets.INRUPT_DEV_CLIENT_ID }}
5048
INRUPT_TEST_CLIENT_SECRET: ${{ secrets.INRUPT_DEV_CLIENT_SECRET }}
5149
INRUPT_TEST_REQUESTER_WEBID: ${{ secrets.INRUPT_DEV_REQUESTER_WEBID }}
5250
INRUPT_TEST_REQUESTER_CLIENT_ID: ${{ secrets.INRUPT_DEV_REQUESTER_CLIENT_ID }}
5351
INRUPT_TEST_REQUESTER_CLIENT_SECRET: ${{ secrets.INRUPT_DEV_REQUESTER_CLIENT_SECRET }}
5452

53+
performance:
54+
name: Performance Tests
55+
runs-on: ubuntu-latest
56+
strategy:
57+
matrix:
58+
java: [ 17 ]
59+
60+
steps:
61+
- uses: actions/checkout@v3
62+
63+
- name: Set up JDK ${{ matrix.java }}
64+
uses: actions/setup-java@v3
65+
with:
66+
distribution: 'temurin'
67+
java-version: ${{ matrix.java }}
68+
cache: 'maven'
69+
70+
- name: Localhost performance tests
71+
if: ${{ github.actor != 'dependabot[bot]' }}
72+
run: mvn -B -ntp verify -Pci -pl performance/uma -am
73+
5574
documentation:
5675
name: Documentation Check
5776
runs-on: ubuntu-latest
Lines changed: 306 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,306 @@
1+
/*
2+
* Copyright Inrupt Inc.
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy
5+
* of this software and associated documentation files (the "Software"), to deal in
6+
* the Software without restriction, including without limitation the rights to use,
7+
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
8+
* Software, and to permit persons to whom the Software is furnished to do so,
9+
* subject to the following conditions:
10+
*
11+
* The above copyright notice and this permission notice shall be included in
12+
* all copies or substantial portions of the Software.
13+
*
14+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
15+
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
16+
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
17+
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
18+
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
19+
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20+
*/
21+
package com.inrupt.client.integration.base;
22+
23+
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
24+
25+
import com.inrupt.client.accessgrant.*;
26+
import com.inrupt.client.auth.Session;
27+
import com.inrupt.client.openid.OpenIdSession;
28+
import com.inrupt.client.solid.*;
29+
import com.inrupt.client.util.URIBuilder;
30+
import com.inrupt.client.vocabulary.ACL;
31+
import com.inrupt.client.webid.WebIdProfile;
32+
33+
import java.io.ByteArrayInputStream;
34+
import java.io.IOException;
35+
import java.io.InputStream;
36+
import java.net.URI;
37+
import java.nio.charset.StandardCharsets;
38+
import java.time.Instant;
39+
import java.util.*;
40+
import java.util.stream.Stream;
41+
42+
import org.eclipse.microprofile.config.Config;
43+
import org.eclipse.microprofile.config.ConfigProvider;
44+
import org.junit.jupiter.api.AfterAll;
45+
import org.junit.jupiter.api.BeforeAll;
46+
import org.junit.jupiter.api.DisplayName;
47+
import org.junit.jupiter.params.ParameterizedTest;
48+
import org.junit.jupiter.params.provider.Arguments;
49+
import org.junit.jupiter.params.provider.MethodSource;
50+
import org.slf4j.Logger;
51+
import org.slf4j.LoggerFactory;
52+
53+
54+
public class PerformanceTest {
55+
56+
private static final Logger LOGGER = LoggerFactory.getLogger(PerformanceTest.class);
57+
private static final int NUMBER_OF_RESOURCES = 2000;
58+
private static final Config config = ConfigProvider.getConfig();
59+
private static MockSolidServer mockHttpServer;
60+
private static MockOpenIDProvider identityProviderServer;
61+
private static MockUMAAuthorizationServer authServer;
62+
private static MockWebIdService webIdService;
63+
private static MockAccessGrantServer accessGrantServer;
64+
65+
private static String podUrl;
66+
private static String issuer;
67+
protected static String webidUrl;
68+
protected static String requesterWebidUrl;
69+
private static final String MOCK_RESOURCE_OWNER_USERNAME = "someuser";
70+
private static final String MOCK_REQUESTER_USERNAME = "requester";
71+
72+
private static final String REQUESTER_CLIENT_ID = config.getValue("inrupt.test.requester.client-id", String.class);
73+
private static final String REQUESTER_CLIENT_SECRET =
74+
config.getValue("inrupt.test.requester.client-secret", String.class);
75+
76+
private static final String RESOURCE_OWNER_CLIENT_ID =
77+
config.getValue("inrupt.test.client-id", String.class);
78+
private static final String RESOURCE_OWNER_CLIENT_SECRET =
79+
config.getValue("inrupt.test.client-secret", String.class);
80+
private static final String AUTH_METHOD = config
81+
.getOptionalValue("inrupt.test.auth-method", String.class)
82+
.orElse("client_secret_basic");
83+
84+
protected static String ACCESS_GRANT_PROVIDER;
85+
protected static final String GRANT_MODE_READ = "Read";
86+
private static final URI PURPOSE1 = URI.create("https://purpose.example/212efdf4-e1a4-4dcd-9d3b-d6eb92e0205f");
87+
private static final URI PURPOSE2 = URI.create("https://purpose.example/de605b08-76c7-4f04-9cec-a438810b0c03");
88+
protected static final Set<URI> PURPOSES = new HashSet<>(Arrays.asList(PURPOSE1, PURPOSE2));
89+
protected static final String GRANT_EXPIRATION = "2024-04-03T12:00:00Z";
90+
private static String sharedTextFileName = "testResource";
91+
protected static URI sharedTextFileURI;
92+
private static URI privateContainerURI;
93+
private static Session requesterSession;
94+
private static Session resourceOwnerSession;
95+
96+
private static SolidSyncClient authResourceOwnerClient;
97+
98+
@BeforeAll
99+
static void setup() {
100+
authServer = new MockUMAAuthorizationServer();
101+
authServer.start();
102+
103+
mockHttpServer = new MockSolidServer(authServer.getMockServerUrl());
104+
mockHttpServer.start();
105+
106+
identityProviderServer = new MockOpenIDProvider(MOCK_RESOURCE_OWNER_USERNAME);
107+
identityProviderServer.start();
108+
109+
webIdService = new MockWebIdService(
110+
mockHttpServer.getMockServerUrl(),
111+
identityProviderServer.getMockServerUrl(),
112+
MOCK_RESOURCE_OWNER_USERNAME);
113+
webIdService.start();
114+
115+
webidUrl = config
116+
.getOptionalValue("inrupt.test.webid", String.class)
117+
.orElse(URIBuilder.newBuilder(URI.create(webIdService.getMockServerUrl()))
118+
.path(MOCK_RESOURCE_OWNER_USERNAME)
119+
.build()
120+
.toString());
121+
122+
State.WEBID = URI.create(webidUrl);
123+
final SolidSyncClient client = SolidSyncClient.getClientBuilder().build();
124+
try (final WebIdProfile profile = client.read(URI.create(webidUrl), WebIdProfile.class)) {
125+
issuer = profile.getOidcIssuers().iterator().next().toString();
126+
podUrl = profile.getStorages().iterator().next().toString();
127+
}
128+
if (!podUrl.endsWith(Utils.FOLDER_SEPARATOR)) {
129+
podUrl += Utils.FOLDER_SEPARATOR;
130+
}
131+
132+
authResourceOwnerClient = createAuthenticatedClient();
133+
privateContainerURI = URIBuilder.newBuilder(URI.create(podUrl))
134+
.path(State.PRIVATE_RESOURCE_PATH + "-performance-test-" + UUID.randomUUID() + "/")
135+
.build();
136+
137+
Utils.createContainer(authResourceOwnerClient, privateContainerURI);
138+
prepareAcpOfResource(authResourceOwnerClient, privateContainerURI, SolidRDFSource.class);
139+
140+
requesterWebidUrl = config
141+
.getOptionalValue("inrupt.test.requester.webid", String.class)
142+
.orElse(URIBuilder.newBuilder(URI.create(webIdService.getMockServerUrl()))
143+
.path(MOCK_REQUESTER_USERNAME)
144+
.build()
145+
.toString());
146+
147+
sharedTextFileURI = URIBuilder.newBuilder(privateContainerURI)
148+
.path(sharedTextFileName)
149+
.build();
150+
151+
accessGrantServer = new MockAccessGrantServer(
152+
URI.create(webidUrl),
153+
URI.create(requesterWebidUrl),
154+
sharedTextFileURI,
155+
authServer.getMockServerUrl()
156+
);
157+
accessGrantServer.start();
158+
159+
ACCESS_GRANT_PROVIDER = config
160+
.getOptionalValue("inrupt.test.access-grant.provider", String.class)
161+
.orElse(accessGrantServer.getMockServerUrl());
162+
163+
LOGGER.info("Integration Test Issuer: [{}]", issuer);
164+
LOGGER.info("Integration Test Pod Host: [{}]", podUrl);
165+
LOGGER.info("Integration Test Access Grant server: [{}]", ACCESS_GRANT_PROVIDER);
166+
}
167+
168+
@AfterAll
169+
static void teardown() {
170+
//cleanup pod
171+
Utils.deleteContentsRecursively(authResourceOwnerClient, privateContainerURI);
172+
173+
mockHttpServer.stop();
174+
identityProviderServer.stop();
175+
authServer.stop();
176+
webIdService.stop();
177+
accessGrantServer.stop();
178+
}
179+
180+
@ParameterizedTest
181+
@MethodSource("provideSessions")
182+
@DisplayName("Measure GET time of resources with grant access")
183+
void measureGetOfResources(final Session resourceOwnerSession, final Session requesterSession) {
184+
185+
LOGGER.info("Performance Test - Measure GET time of resources with grant access");
186+
187+
createResources(authResourceOwnerClient, privateContainerURI);
188+
189+
final AccessGrantClient requesterAccessGrantClient = new AccessGrantClient(
190+
URI.create(ACCESS_GRANT_PROVIDER)
191+
).session(requesterSession);
192+
193+
final AccessGrantClient resourceOwnerAccessGrantClient = new AccessGrantClient(
194+
URI.create(ACCESS_GRANT_PROVIDER)
195+
).session(resourceOwnerSession);
196+
197+
final Set<String> modes = new HashSet<>(Arrays.asList(GRANT_MODE_READ));
198+
final Instant expiration = Instant.parse(GRANT_EXPIRATION);
199+
final AccessRequest request = requesterAccessGrantClient.requestAccess(privateContainerURI,
200+
new HashSet<>(Arrays.asList(privateContainerURI)), modes, PURPOSES, expiration)
201+
.toCompletableFuture().join();
202+
final AccessGrant grant = resourceOwnerAccessGrantClient.grantAccess(request)
203+
.toCompletableFuture().join();
204+
205+
final Session accessSession = AccessGrantSession.ofAccessGrant(requesterSession, grant);
206+
final SolidSyncClient requesterAuthClient =
207+
SolidSyncClient.getClientBuilder().build().session(accessSession);
208+
209+
for (int j = 0; j < 9; j++) {
210+
LOGGER.info("Time of performance test, no cache start");
211+
final long[] result = new long[NUMBER_OF_RESOURCES];
212+
for (int i = 0; i < NUMBER_OF_RESOURCES; i++) {
213+
214+
//authorized request test
215+
final URI resourceURI = URIBuilder.newBuilder(privateContainerURI)
216+
.path(sharedTextFileName + j + i)
217+
.build();
218+
219+
final long start = System.nanoTime();
220+
requesterAuthClient.read(resourceURI, SolidNonRDFSource.class);
221+
final long end = System.nanoTime();
222+
result[i] = end - start;
223+
}
224+
LOGGER.info("Average time of all {} GETs : {} in nanosec", NUMBER_OF_RESOURCES,
225+
Arrays.stream(result).average().getAsDouble());
226+
}
227+
228+
for (int i = 0; i < NUMBER_OF_RESOURCES; i++) {
229+
assertDoesNotThrow(resourceOwnerAccessGrantClient.revoke(grant).toCompletableFuture()::join);
230+
}
231+
}
232+
233+
private static void createResources(final SolidSyncClient client, final URI privateContainerURI) {
234+
for (int j = 0; j < 9; j++) {
235+
for (int i = 0; i < NUMBER_OF_RESOURCES; i++) {
236+
final URI resourceURI = URIBuilder.newBuilder(privateContainerURI)
237+
.path(sharedTextFileName + j + i)
238+
.build();
239+
//create test file in access grant enabled container
240+
try (final InputStream is =
241+
new ByteArrayInputStream(StandardCharsets.UTF_8.encode("Test text").array())) {
242+
final SolidNonRDFSource testResource =
243+
new SolidNonRDFSource(resourceURI, Utils.PLAIN_TEXT, is, null);
244+
assertDoesNotThrow(() -> client.create(testResource));
245+
} catch (IOException e) {
246+
LOGGER.warn("Could not create performance test resource " + resourceURI);
247+
}
248+
}
249+
}
250+
}
251+
252+
private static <T extends SolidResource> void prepareAcpOfResource(final SolidSyncClient authClient,
253+
final URI resourceURI,
254+
final Class<T> clazz) {
255+
SolidResource resource = null;
256+
try {
257+
if (SolidNonRDFSource.class.isAssignableFrom(clazz)) {
258+
resource = authClient.read(resourceURI, SolidNonRDFSource.class);
259+
} else if (SolidRDFSource.class.isAssignableFrom(clazz)) {
260+
resource = authClient.read(resourceURI, SolidRDFSource.class);
261+
}
262+
if (resource != null) {
263+
// find the acl Link in the header of the resource
264+
resource.getMetadata().getAcl().ifPresent(acl -> {
265+
try (final SolidRDFSource acr = authClient.read(acl, SolidRDFSource.class)) {
266+
AccessGrantUtils.accessControlPolicyTriples(acl, ACL.Read, ACL.Write)
267+
.forEach(acr.getGraph()::add);
268+
authClient.update(acr);
269+
}
270+
});
271+
resource.close();
272+
}
273+
} catch (Exception e) {
274+
throw new RuntimeException(e);
275+
}
276+
}
277+
278+
private static Stream<Arguments> provideSessions() throws SolidClientException {
279+
resourceOwnerSession = OpenIdSession.ofClientCredentials(
280+
URI.create(issuer),
281+
RESOURCE_OWNER_CLIENT_ID,
282+
RESOURCE_OWNER_CLIENT_SECRET,
283+
AUTH_METHOD);
284+
285+
requesterSession = OpenIdSession.ofClientCredentials(
286+
URI.create(issuer),
287+
REQUESTER_CLIENT_ID,
288+
REQUESTER_CLIENT_SECRET,
289+
AUTH_METHOD);
290+
291+
return Stream.of(
292+
Arguments.of(resourceOwnerSession, requesterSession)
293+
);
294+
}
295+
296+
private static SolidSyncClient createAuthenticatedClient() {
297+
final Session session = OpenIdSession.ofClientCredentials(
298+
URI.create(issuer), //Client credentials
299+
RESOURCE_OWNER_CLIENT_ID,
300+
RESOURCE_OWNER_CLIENT_SECRET,
301+
AUTH_METHOD);
302+
303+
return SolidSyncClient.getClient().session(session);
304+
}
305+
306+
}

integration/base/src/site/site.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<project name="Inrupt Java Client Libraries: Demos">
1+
<project name="Inrupt Java Client Libraries: Integration Tests">
22

33
<bannerRight>
44
<name>Inrupt</name>

integration/openid/src/site/site.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<project name="Inrupt Java Client Libraries: Demos">
1+
<project name="Inrupt Java Client Libraries: OpenId Integration Tests">
22

33
<bannerRight>
44
<name>Inrupt</name>

integration/src/site/site.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<project name="Inrupt Java Client Libraries: Demos">
1+
<project name="Inrupt Java Client Libraries: Integration Tests">
22

33
<bannerRight>
44
<name>Inrupt</name>

integration/uma/src/site/site.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<project name="Inrupt Java Client Libraries: Demos">
1+
<project name="Inrupt Java Client Libraries: UMA Integration Tests">
22

33
<bannerRight>
44
<name>Inrupt</name>

0 commit comments

Comments
 (0)