Skip to content

Commit 6d874af

Browse files
authored
Add RealJenkins Test for RestEasy client to parse a api/v4/user response (#1880)
* Add test for RestEasy client to parse a api/v4/user response * Convert the ResteasyGitLabClientBuilderGetCurrentUserTest to RealJenkinsExtension to replicate class loading on real instances
1 parent 1d7f3dc commit 6d874af

1 file changed

Lines changed: 202 additions & 0 deletions

File tree

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
package com.dabsquared.gitlabjenkins.gitlab.api.impl;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.mockserver.model.HttpRequest.request;
5+
import static org.mockserver.model.HttpResponse.response;
6+
7+
import com.cloudbees.plugins.credentials.CredentialsProvider;
8+
import com.cloudbees.plugins.credentials.CredentialsScope;
9+
import com.cloudbees.plugins.credentials.CredentialsStore;
10+
import com.cloudbees.plugins.credentials.SystemCredentialsProvider;
11+
import com.cloudbees.plugins.credentials.domains.Domain;
12+
import com.dabsquared.gitlabjenkins.connection.GitlabCredentialResolver;
13+
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabClient;
14+
import com.dabsquared.gitlabjenkins.gitlab.api.model.User;
15+
import hudson.util.Secret;
16+
import jakarta.ws.rs.HttpMethod;
17+
import jakarta.ws.rs.core.MediaType;
18+
import jakarta.ws.rs.core.Response;
19+
import java.io.Serial;
20+
import java.io.Serializable;
21+
import java.util.List;
22+
import java.util.function.Consumer;
23+
import jenkins.model.Jenkins;
24+
import org.jenkinsci.plugins.plaincredentials.impl.StringCredentialsImpl;
25+
import org.junit.jupiter.api.AfterEach;
26+
import org.junit.jupiter.api.BeforeAll;
27+
import org.junit.jupiter.api.Test;
28+
import org.junit.jupiter.api.extension.ExtendWith;
29+
import org.junit.jupiter.api.extension.RegisterExtension;
30+
import org.jvnet.hudson.test.JenkinsRule;
31+
import org.jvnet.hudson.test.junit.jupiter.RealJenkinsExtension;
32+
import org.mockserver.client.MockServerClient;
33+
import org.mockserver.junit.jupiter.MockServerExtension;
34+
35+
@ExtendWith(MockServerExtension.class)
36+
class ResteasyGitLabClientBuilderGetCurrentUserTest {
37+
38+
private static final String API_TOKEN = "secret";
39+
private static final String API_TOKEN_ID = "apiTokenId";
40+
41+
private static MockServerClient mockServerClient;
42+
43+
@RegisterExtension
44+
private final RealJenkinsExtension rule = new RealJenkinsExtension();
45+
46+
@BeforeAll
47+
static void setUpMockServer(MockServerClient mockServer) {
48+
mockServerClient = mockServer;
49+
}
50+
51+
@AfterEach
52+
void tearDown() {
53+
mockServerClient.reset();
54+
}
55+
56+
@Test
57+
void getCurrentUser_deserializesJsonIntoUser() throws Throwable {
58+
String userJson = """
59+
{
60+
"id": 42,
61+
"name": "Jen Kins",
62+
"username": "jenkins",
63+
"email": "jenkins@noreply.gitlab.example.com"
64+
}""";
65+
66+
setupMockServerClient(userJson);
67+
68+
rule.then(new DeserializesJsonIntoUserStep(mockServerClient.getPort(), user -> {
69+
assertEquals(42, user.getId());
70+
assertEquals("Jen Kins", user.getName());
71+
assertEquals("jenkins", user.getUsername());
72+
assertEquals("jenkins@noreply.gitlab.example.com", user.getEmail());
73+
}));
74+
}
75+
76+
@Test
77+
void getCurrentUser_ignoresUnknownJsonFields() throws Throwable {
78+
String userJson = """
79+
{
80+
"id": 42,
81+
"username": "jenkins",
82+
"public_email": null,
83+
"name": "Jen Kins",
84+
"state": "active",
85+
"locked": false,
86+
"avatar_url": "",
87+
"web_url": "https://gitlab.example.com/jenkins",
88+
"created_at": "2026-03-30T14:08:23.172Z",
89+
"bio": "",
90+
"location": "",
91+
"linkedin": "",
92+
"twitter": "",
93+
"discord": "",
94+
"website_url": "",
95+
"github": "",
96+
"job_title": "",
97+
"pronouns": null,
98+
"organization": "",
99+
"bot": true,
100+
"work_information": null,
101+
"local_time": null,
102+
"last_sign_in_at": null,
103+
"confirmed_at": "2026-03-30T14:08:23.172Z",
104+
"last_activity_on": "2026-03-30",
105+
"email": "jenkins@noreply.gitlab.example.com",
106+
"theme_id": 3,
107+
"color_scheme_id": 1,
108+
"projects_limit": 10,
109+
"current_sign_in_at": null,
110+
"identities": [],
111+
"can_create_group": true,
112+
"can_create_project": true,
113+
"two_factor_enabled": false,
114+
"external": false,
115+
"private_profile": false,
116+
"commit_email": "jenkins@noreply.gitlab.example.com",
117+
"preferred_language": "en"
118+
}""";
119+
120+
setupMockServerClient(userJson);
121+
122+
rule.then(new DeserializesJsonIntoUserStep(mockServerClient.getPort(), user -> {
123+
assertEquals(42, user.getId());
124+
assertEquals("Jen Kins", user.getName());
125+
assertEquals("jenkins", user.getUsername());
126+
assertEquals("jenkins@noreply.gitlab.example.com", user.getEmail());
127+
}));
128+
}
129+
130+
private void setupMockServerClient(final String userResponseJson) {
131+
mockServerClient
132+
.when(request()
133+
.withMethod(HttpMethod.GET)
134+
.withPath("/gitlab/api/v4/user")
135+
.withHeader("PRIVATE-TOKEN", API_TOKEN))
136+
.respond(response()
137+
.withStatusCode(Response.Status.OK.getStatusCode())
138+
.withHeader("Content-Type", MediaType.APPLICATION_JSON)
139+
.withBody(userResponseJson));
140+
}
141+
142+
/**
143+
* Serializable test run class that carries the MockServer port into the Jenkins JVM.
144+
*/
145+
private static final class DeserializesJsonIntoUserStep implements RealJenkinsExtension.Step {
146+
@Serial
147+
private static final long serialVersionUID = 1L;
148+
149+
private final int port;
150+
151+
private final SerializableConsumer<User> assertUser;
152+
153+
public DeserializesJsonIntoUserStep(final int port, final SerializableConsumer<User> assertUser) {
154+
this.port = port;
155+
this.assertUser = assertUser;
156+
}
157+
158+
private int getPort() {
159+
return port;
160+
}
161+
162+
@Override
163+
public void run(JenkinsRule r) throws Throwable {
164+
addGitLabApiToken();
165+
166+
User user = buildGitLabClient(getPort()).getCurrentUser();
167+
168+
assertUser.accept(user);
169+
}
170+
171+
/**
172+
* Own version of this utility method, because with {@link RealJenkinsExtension} the test class runs in a
173+
* different classloader than the plugin code.
174+
*/
175+
static void addGitLabApiToken() throws Exception {
176+
for (CredentialsStore credentialsStore : CredentialsProvider.lookupStores(Jenkins.getInstanceOrNull())) {
177+
if (credentialsStore instanceof SystemCredentialsProvider.StoreImpl) {
178+
List<Domain> domains = credentialsStore.getDomains();
179+
credentialsStore.addCredentials(
180+
domains.get(0),
181+
new StringCredentialsImpl(
182+
CredentialsScope.SYSTEM,
183+
API_TOKEN_ID,
184+
"GitLab API Token",
185+
Secret.fromString(API_TOKEN)));
186+
}
187+
}
188+
}
189+
190+
/**
191+
* Own version of this utility method, because with {@link RealJenkinsExtension} the test class runs in a
192+
* different classloader than the plugin code.
193+
*/
194+
static GitLabClient buildGitLabClient(final int port) {
195+
String gitLabUrl = "http://localhost:" + port + "/gitlab";
196+
return new V4GitLabClientBuilder()
197+
.buildClient(gitLabUrl, new GitlabCredentialResolver(null, API_TOKEN_ID), true, 10, 10);
198+
}
199+
}
200+
201+
interface SerializableConsumer<T> extends Consumer<T>, Serializable {}
202+
}

0 commit comments

Comments
 (0)