Skip to content

Commit aa70b56

Browse files
committed
Changed so the tests wait time is reduced by using TimeService to control the sleep time for threads. Fixed the agent_spiffe_cert.pem
1 parent c21c663 commit aa70b56

File tree

5 files changed

+200
-122
lines changed

5 files changed

+200
-122
lines changed

oauth2_http/java/com/google/auth/oauth2/AgentIdentityUtils.java

Lines changed: 53 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,10 @@ final class AgentIdentityUtils {
6969
Pattern.compile("^agents\\.global\\.proj-\\d+\\.system\\.id\\.goog$"));
7070

7171
// Polling configuration
72-
static long TOTAL_TIMEOUT_MS = 30000; // 30 seconds
73-
static long FAST_POLL_DURATION_MS = 5000; // 5 seconds
74-
static long FAST_POLL_INTERVAL_MS = 100; // 0.1 seconds
75-
static long SLOW_POLL_INTERVAL_MS = 500; // 0.5 seconds
72+
private static final long TOTAL_TIMEOUT_MS = 30000; // 30 seconds
73+
private static final long FAST_POLL_DURATION_MS = 5000; // 5 seconds
74+
private static final long FAST_POLL_INTERVAL_MS = 100; // 0.1 seconds
75+
private static final long SLOW_POLL_INTERVAL_MS = 500; // 0.5 seconds
7676

7777
private static final int SAN_URI_TYPE = 6;
7878
private static final String SPIFFE_SCHEME_PREFIX = "spiffe://";
@@ -84,6 +84,30 @@ interface EnvReader {
8484

8585
private static EnvReader envReader = System::getenv;
8686

87+
/**
88+
* Internal interface to allow mocking time and sleep for tests. This is used to prevent tests
89+
* from running for long periods of time when polling is involved.
90+
*/
91+
@VisibleForTesting
92+
interface TimeService {
93+
long currentTimeMillis();
94+
95+
void sleep(long millis) throws InterruptedException;
96+
}
97+
98+
private static TimeService timeService =
99+
new TimeService() {
100+
@Override
101+
public long currentTimeMillis() {
102+
return System.currentTimeMillis();
103+
}
104+
105+
@Override
106+
public void sleep(long millis) throws InterruptedException {
107+
Thread.sleep(millis);
108+
}
109+
};
110+
87111
private AgentIdentityUtils() {}
88112

89113
/**
@@ -114,7 +138,7 @@ private static boolean isOptedOut() {
114138

115139
/** Polls for the certificate config file and the certificate file it references. */
116140
private static String getCertificatePathWithRetry(String certConfigPath) throws IOException {
117-
long startTime = System.currentTimeMillis();
141+
long startTime = timeService.currentTimeMillis();
118142
boolean warned = false;
119143

120144
while (true) {
@@ -127,10 +151,10 @@ private static String getCertificatePathWithRetry(String certConfigPath) throws
127151
}
128152
} catch (Exception e) {
129153
// Ignore exceptions during polling and retry
130-
LOGGER.log(Level.FINE, "Error while polling for certificate files", e);
154+
LOGGER.log(Level.FINE, "Error while polling for certificate files");
131155
}
132156

133-
long elapsedTime = System.currentTimeMillis() - startTime;
157+
long elapsedTime = timeService.currentTimeMillis() - startTime;
134158
if (elapsedTime >= TOTAL_TIMEOUT_MS) {
135159
throw new IOException(
136160
"Certificate config or certificate file not found after multiple retries. "
@@ -151,7 +175,7 @@ private static String getCertificatePathWithRetry(String certConfigPath) throws
151175
try {
152176
long sleepTime =
153177
elapsedTime < FAST_POLL_DURATION_MS ? FAST_POLL_INTERVAL_MS : SLOW_POLL_INTERVAL_MS;
154-
Thread.sleep(sleepTime);
178+
timeService.sleep(sleepTime);
155179
} catch (InterruptedException e) {
156180
Thread.currentThread().interrupt();
157181
throw new IOException("Interrupted while waiting for certificate files", e);
@@ -239,4 +263,25 @@ static String calculateCertificateFingerprint(X509Certificate cert) throws IOExc
239263
static void setEnvReader(EnvReader reader) {
240264
envReader = reader;
241265
}
266+
267+
@VisibleForTesting
268+
static void setTimeService(TimeService service) {
269+
timeService = service;
270+
}
271+
272+
@VisibleForTesting
273+
static void resetTimeService() {
274+
timeService =
275+
new TimeService() {
276+
@Override
277+
public long currentTimeMillis() {
278+
return System.currentTimeMillis();
279+
}
280+
281+
@Override
282+
public void sleep(long millis) throws InterruptedException {
283+
Thread.sleep(millis);
284+
}
285+
};
286+
}
242287
}

oauth2_http/javatests/com/google/auth/oauth2/AgentIdentityUtilsTest.java

Lines changed: 25 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.util.Collection;
2323
import java.util.Collections;
2424
import java.util.List;
25+
import java.util.concurrent.atomic.AtomicLong;
2526
import org.junit.After;
2627
import org.junit.Before;
2728
import org.junit.Test;
@@ -39,49 +40,20 @@ public class AgentIdentityUtilsTest {
3940
private static final String INVALID_SPIFFE_FORMAT =
4041
"spiffe://agents.global.org-INVALID.system.id.goog/path";
4142

42-
// A minimal, valid self-signed X.509 certificate (PEM format) for testing loading.
43-
// Generated for testing purposes.
44-
private static final String TEST_CERT_PEM =
45-
"-----BEGIN CERTIFICATE-----\n"
46-
+ "MIIDWTCCAkGgAwIBAgIUX5/1aT1uuxgj1+F7Q/r+5Q9y4JQwDQYJKoZIhvcNAQEL\n"
47-
+ "BQAwHTEbMBkGA1UEAwwSdGVzdC5leGFtcGxlLmNvbTAeFw0yNDAxMDEwMDAwMDBa\n"
48-
+ "Fw0zNDAxMDEwMDAwMDBaMB0xGzAZBgNVBAMMEnRlc3QuZXhhbXBsZS5jb20wggEi\n"
49-
+ "MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDV/8Q/5+8+X9Y+5+6+7+8+9+0+\n"
50-
+ "A/B/C/D/E/F/G/H/I/J/K/L/M/N/O/P/Q/R/S/T/U/V/W/X/Y/Z/a/b/c/d/e/f/\n"
51-
+ "g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/0/1/2/3/4/5/6/7/8/9/+A/B\n"
52-
+ "/C/D/E/F/G/H/I/J/K/L/M/N/O/P/Q/R/S/T/U/V/W/X/Y/Z/a/b/c/d/e/f/g/h\n"
53-
+ "/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/0/1/2/3/4/5/6/7/8/9/+A/B/C/\n"
54-
+ "D/E/F/G/H/I/J/K/L/M/N/O/P/Q/R/S/T/U/V/W/X/Y/Z/a/b/c/d/e/f/g/h/i/\n"
55-
+ "j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/0/1/2/3/4/5/6/7/8/9/+A/B/C/D/E\n"
56-
+ "AgMBAAGjUzBRMB0GA1UdDgQWBBS/1/2/3/4/5/6/7/8/9/+A/B/C/DAfBgNVHSME\n"
57-
+ "GDAWgBS/1/2/3/4/5/6/7/8/9/+A/B/C/DAPBgNVHRMBAf8EBTADAQH/MA0GCSqG\n"
58-
+ "SIb3DQEBCwUAA4IBAQDV/8Q/5+8+X9Y+5+6+7+8+9+0+A/B/C/D/E/F/G/H/I/J/\n"
59-
+ "K/L/M/N/O/P/Q/R/S/T/U/V/W/X/Y/Z/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/\n"
60-
+ "q/r/s/t/u/v/w/x/y/z/0/1/2/3/4/5/6/7/8/9/+A/B/C/D/E/F/G/H/I/J/K/L\n"
61-
+ "/M/N/O/P/Q/R/S/T/U/V/W/X/Y/Z/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r\n"
62-
+ "/s/t/u/v/w/x/y/z/0/1/2/3/4/5/6/7/8/9/+A/B/C/D/E/F/G/H/I/J/K/L/M/\n"
63-
+ "N/O/P/Q/R/S/T/U/V/W/X/Y/Z/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/\n"
64-
+ "t/u/v/w/x/y/z/0/1/2/3/4/5/6/7/8/9/+A/B/C/D/E\n"
65-
+ "-----END CERTIFICATE-----";
66-
6743
private TestEnvironmentProvider envProvider;
6844
private Path tempDir;
6945

7046
@Before
7147
public void setUp() throws IOException {
7248
envProvider = new TestEnvironmentProvider();
73-
// Inject our test environment reader
7449
AgentIdentityUtils.setEnvReader(envProvider::getEnv);
7550
tempDir = Files.createTempDirectory("agent_identity_test");
7651
}
7752

7853
@After
7954
public void tearDown() throws IOException {
80-
// Reset polling constants to defaults after each test to avoid side effects
81-
AgentIdentityUtils.TOTAL_TIMEOUT_MS = 30000;
82-
AgentIdentityUtils.FAST_POLL_INTERVAL_MS = 100;
83-
AgentIdentityUtils.FAST_POLL_DURATION_MS = 5000;
84-
AgentIdentityUtils.SLOW_POLL_INTERVAL_MS = 500;
55+
// Reset the time service to default after each test
56+
AgentIdentityUtils.resetTimeService();
8557

8658
// Clean up temp files
8759
if (tempDir != null) {
@@ -176,8 +148,8 @@ public void getAgentIdentityCertificate_noConfigEnvVar_returnsNull() throws IOEx
176148
@Test
177149
public void getAgentIdentityCertificate_happyPath_loadsCertificate() throws IOException {
178150
// Setup: Get the absolute path of the test resource.
179-
URL certUrl = getClass().getClassLoader().getResource("agent_cert.pem");
180-
assertNotNull("Test resource agent_cert.pem not found", certUrl);
151+
URL certUrl = getClass().getClassLoader().getResource("x509_leaf_certificate.pem");
152+
assertNotNull("Test resource x509_leaf_certificate.pem not found", certUrl);
181153
String certPath = new File(certUrl.getFile()).getAbsolutePath();
182154

183155
// Create config file pointing to the cert.
@@ -206,7 +178,7 @@ public void getAgentIdentityCertificate_happyPath_loadsCertificate() throws IOEx
206178

207179
// Verify
208180
assertNotNull(cert);
209-
// Basic verification that it loaded OUR cert (checking issuer from agent_cert.pem)
181+
// Basic verification that it loaded OUR cert
210182
assertTrue(cert.getIssuerDN().getName().contains("unit-tests"));
211183
}
212184

@@ -217,11 +189,9 @@ public void getAgentIdentityCertificate_timeout_throwsIOException() {
217189
"GOOGLE_API_CERTIFICATE_CONFIG",
218190
tempDir.resolve("missing.json").toAbsolutePath().toString());
219191

220-
// Reduce timeout to make test fast (e.g., 100ms total)
221-
AgentIdentityUtils.TOTAL_TIMEOUT_MS = 100;
222-
AgentIdentityUtils.FAST_POLL_INTERVAL_MS = 10;
223-
AgentIdentityUtils.SLOW_POLL_INTERVAL_MS = 10;
224-
AgentIdentityUtils.FAST_POLL_DURATION_MS = 50;
192+
// Use a fake time service that advances time rapidly when sleep is called.
193+
// This allows the 30s timeout loop to complete instantly in test execution time.
194+
AgentIdentityUtils.setTimeService(new FakeTimeService());
225195

226196
// Execute & Verify
227197
IOException e =
@@ -231,6 +201,22 @@ public void getAgentIdentityCertificate_timeout_throwsIOException() {
231201
.contains("Certificate config or certificate file not found after multiple retries"));
232202
}
233203

204+
// Fake time service that advances time when sleep is requested.
205+
private static class FakeTimeService implements AgentIdentityUtils.TimeService {
206+
private final AtomicLong currentTime = new AtomicLong(0);
207+
208+
@Override
209+
public long currentTimeMillis() {
210+
return currentTime.get();
211+
}
212+
213+
@Override
214+
public void sleep(long millis) throws InterruptedException {
215+
// Instead of actually sleeping, just advance the fake clock.
216+
currentTime.addAndGet(millis);
217+
}
218+
}
219+
234220
// A helper class to mock System.getenv for testing purposes within this file.
235221
private static class TestEnvironmentProvider {
236222
private final java.util.Map<String, String> env = new java.util.HashMap<>();

0 commit comments

Comments
 (0)