add tests

This commit is contained in:
Krrish Ghimire
2026-07-10 15:44:48 +05:45
parent e705f240d4
commit e684154938
7 changed files with 771 additions and 0 deletions

View File

@@ -0,0 +1,193 @@
package com.krrishg.config;
import com.krrishg.model.User;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.KeyPair;
import java.util.UUID;
import static io.jsonwebtoken.Jwts.SIG.RS256;
import static org.junit.jupiter.api.Assertions.*;
class JwtConfigTest {
@Test
void generateAndValidateAccessToken() {
JwtConfig jwtConfig = createWithExpiration(900_000L, 604_800_000L);
jwtConfig.init();
User user = User.builder()
.id(UUID.randomUUID())
.email("test@example.com")
.name("Test User")
.build();
String token = jwtConfig.generateAccessToken(user);
Claims claims = jwtConfig.validateToken(token);
assertEquals(user.getId().toString(), claims.getSubject());
assertEquals("test@example.com", claims.get("email", String.class));
assertEquals("Test User", claims.get("name", String.class));
assertNotNull(claims.getIssuedAt());
assertNotNull(claims.getExpiration());
}
@Test
void generateAndValidateRefreshToken() {
JwtConfig jwtConfig = createWithExpiration(900_000L, 604_800_000L);
jwtConfig.init();
User user = User.builder()
.id(UUID.randomUUID())
.email("refresh@example.com")
.name("Refresh User")
.build();
String token = jwtConfig.generateRefreshToken(user);
Claims claims = jwtConfig.validateToken(token);
assertEquals(user.getId().toString(), claims.getSubject());
assertEquals("refresh@example.com", claims.get("email", String.class));
assertEquals("Refresh User", claims.get("name", String.class));
}
@Test
void multipleTokensForSameUserAreAllValid() {
JwtConfig jwtConfig = createWithExpiration(900_000L, 604_800_000L);
jwtConfig.init();
User user = User.builder()
.id(UUID.randomUUID())
.email("same@example.com")
.build();
String token1 = jwtConfig.generateAccessToken(user);
String token2 = jwtConfig.generateAccessToken(user);
assertDoesNotThrow(() -> jwtConfig.validateToken(token1));
assertDoesNotThrow(() -> jwtConfig.validateToken(token2));
Claims claims1 = jwtConfig.validateToken(token1);
Claims claims2 = jwtConfig.validateToken(token2);
assertEquals(user.getId().toString(), claims1.getSubject());
assertEquals(user.getId().toString(), claims2.getSubject());
}
@Test
void rejectTamperedToken() {
JwtConfig jwtConfig = createWithExpiration(900_000L, 604_800_000L);
jwtConfig.init();
User user = User.builder()
.id(UUID.randomUUID())
.email("tamper@example.com")
.build();
String token = jwtConfig.generateAccessToken(user);
String tampered = token.substring(0, token.lastIndexOf('.') + 1) + "tampered";
assertThrows(Exception.class, () -> jwtConfig.validateToken(tampered));
}
@Test
void rejectExpiredToken() throws Exception {
JwtConfig jwtConfig = new JwtConfig();
setField(jwtConfig, "accessTokenExpiration", -1000L);
jwtConfig.init();
User user = User.builder()
.id(UUID.randomUUID())
.email("expired@example.com")
.build();
String token = jwtConfig.generateAccessToken(user);
assertThrows(Exception.class, () -> jwtConfig.validateToken(token));
}
@Test
void initGeneratesEphemeralKeys() {
JwtConfig jwtConfig = createWithExpiration(900_000L, 604_800_000L);
jwtConfig.init();
assertNotNull(jwtConfig.getPublicKey());
User user = User.builder().id(UUID.randomUUID()).email("ephemeral@test.com").build();
String token = jwtConfig.generateAccessToken(user);
assertDoesNotThrow(() -> jwtConfig.validateToken(token));
}
@Test
void initLoadsKeysFromFiles(@TempDir Path tempDir) throws Exception {
KeyPair keyPair = RS256.keyPair().build();
Path privPath = tempDir.resolve("private.key");
Path pubPath = tempDir.resolve("public.key");
Files.write(privPath, keyPair.getPrivate().getEncoded());
Files.write(pubPath, keyPair.getPublic().getEncoded());
JwtConfig jwtConfig = new JwtConfig();
setField(jwtConfig, "privateKeyPath", privPath.toString());
setField(jwtConfig, "publicKeyPath", pubPath.toString());
setField(jwtConfig, "accessTokenExpiration", 900_000L);
setField(jwtConfig, "refreshTokenExpiration", 604_800_000L);
jwtConfig.init();
User user = User.builder().id(UUID.randomUUID()).email("file@test.com").name("File Test").build();
String token = jwtConfig.generateAccessToken(user);
Claims claims = jwtConfig.validateToken(token);
assertEquals(user.getId().toString(), claims.getSubject());
assertEquals("file@test.com", claims.get("email", String.class));
assertEquals("File Test", claims.get("name", String.class));
}
@Test
void initGeneratesAndSavesKeysWhenFilesDoNotExist(@TempDir Path tempDir) throws Exception {
Path privPath = tempDir.resolve("private.key");
Path pubPath = tempDir.resolve("public.key");
JwtConfig jwtConfig = new JwtConfig();
setField(jwtConfig, "privateKeyPath", privPath.toString());
setField(jwtConfig, "publicKeyPath", pubPath.toString());
setField(jwtConfig, "accessTokenExpiration", 900_000L);
setField(jwtConfig, "refreshTokenExpiration", 604_800_000L);
jwtConfig.init();
assertTrue(Files.exists(privPath));
assertTrue(Files.exists(pubPath));
User user = User.builder().id(UUID.randomUUID()).email("saved@test.com").build();
String token = jwtConfig.generateAccessToken(user);
assertDoesNotThrow(() -> jwtConfig.validateToken(token));
}
@Test
void getExpirationReturnsConfiguredValues() throws Exception {
JwtConfig jwtConfig = new JwtConfig();
setField(jwtConfig, "accessTokenExpiration", 300_000L);
setField(jwtConfig, "refreshTokenExpiration", 1_800_000L);
assertEquals(300_000L, jwtConfig.getAccessTokenExpiration());
assertEquals(1_800_000L, jwtConfig.getRefreshTokenExpiration());
}
private JwtConfig createWithExpiration(long access, long refresh) {
JwtConfig jwtConfig = new JwtConfig();
try {
setField(jwtConfig, "accessTokenExpiration", access);
setField(jwtConfig, "refreshTokenExpiration", refresh);
} catch (Exception e) {
throw new RuntimeException(e);
}
return jwtConfig;
}
private void setField(Object target, String name, Object value) throws Exception {
Field field = target.getClass().getDeclaredField(name);
field.setAccessible(true);
field.set(target, value);
}
}

View File

@@ -0,0 +1,60 @@
package com.krrishg.config;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.jdbc.core.JdbcTemplate;
import javax.sql.DataSource;
import java.sql.SQLException;
import static org.junit.jupiter.api.Assertions.*;
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class SchemaMigrationTest {
@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
private DataSource dataSource;
@Test
void migrateExecutesWithoutError() {
SchemaMigration migration = new SchemaMigration(jdbcTemplate);
assertDoesNotThrow(() -> migration.migrate());
}
@Test
void migrateIsIdempotent() {
SchemaMigration migration = new SchemaMigration(jdbcTemplate);
migration.migrate();
assertDoesNotThrow(() -> migration.migrate());
}
@Test
void sslColumnsExistOnSelfHostedConfigs() throws SQLException {
SchemaMigration migration = new SchemaMigration(jdbcTemplate);
migration.migrate();
assertTrue(columnExists("self_hosted_configs", "ssl_enabled"));
assertTrue(columnExists("self_hosted_configs", "ssl_email"));
}
@Test
void encryptedSshKeyColumnIsDropped() throws SQLException {
SchemaMigration migration = new SchemaMigration(jdbcTemplate);
migration.migrate();
assertFalse(columnExists("self_hosted_configs", "encrypted_ssh_key"));
}
private boolean columnExists(String table, String column) throws SQLException {
try (var rs = dataSource.getConnection().getMetaData()
.getColumns(null, null, table, column)) {
return rs.next();
}
}
}

View File

@@ -0,0 +1,144 @@
package com.krrishg.config;
import io.jsonwebtoken.Claims;
import jakarta.servlet.FilterChain;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;
import java.lang.reflect.Method;
import java.util.UUID;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
class SecurityConfigTest {
@Mock
private JwtConfig jwtConfig;
private SecurityConfig securityConfig;
@BeforeEach
void setUp() {
securityConfig = new SecurityConfig(jwtConfig);
}
@AfterEach
void tearDown() {
SecurityContextHolder.clearContext();
}
@Test
void setsAuthenticationForValidToken() throws Exception {
UUID userId = UUID.randomUUID();
Claims claims = mock(Claims.class);
when(claims.getSubject()).thenReturn(userId.toString());
when(claims.get("email", String.class)).thenReturn("test@example.com");
when(claims.get("name", String.class)).thenReturn("Test User");
when(jwtConfig.validateToken("valid-token")).thenReturn(claims);
OncePerRequestFilter filter = getJwtFilter();
HttpServletRequest request = mock(HttpServletRequest.class);
when(request.getHeader("Authorization")).thenReturn("Bearer valid-token");
HttpServletResponse response = mock(HttpServletResponse.class);
FilterChain chain = mock(FilterChain.class);
filter.doFilter(request, response, chain);
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
assertInstanceOf(JwtAuthenticationToken.class, auth);
UserPrincipal principal = (UserPrincipal) auth.getPrincipal();
assertEquals(userId, principal.id());
assertEquals("test@example.com", principal.email());
assertEquals("Test User", principal.name());
verify(chain).doFilter(request, response);
}
@Test
void clearsContextOnInvalidToken() throws Exception {
when(jwtConfig.validateToken(anyString())).thenThrow(new RuntimeException("bad token"));
OncePerRequestFilter filter = getJwtFilter();
HttpServletRequest request = mock(HttpServletRequest.class);
when(request.getHeader("Authorization")).thenReturn("Bearer bad-token");
HttpServletResponse response = mock(HttpServletResponse.class);
FilterChain chain = mock(FilterChain.class);
filter.doFilter(request, response, chain);
assertNull(SecurityContextHolder.getContext().getAuthentication());
verify(chain).doFilter(request, response);
}
@Test
void doesNotAuthenticateWhenNoAuthHeader() throws Exception {
OncePerRequestFilter filter = getJwtFilter();
HttpServletRequest request = mock(HttpServletRequest.class);
when(request.getHeader("Authorization")).thenReturn(null);
HttpServletResponse response = mock(HttpServletResponse.class);
FilterChain chain = mock(FilterChain.class);
filter.doFilter(request, response, chain);
assertNull(SecurityContextHolder.getContext().getAuthentication());
verify(chain).doFilter(request, response);
verify(jwtConfig, never()).validateToken(any());
}
@Test
void doesNotAuthenticateForNonBearerScheme() throws Exception {
OncePerRequestFilter filter = getJwtFilter();
HttpServletRequest request = mock(HttpServletRequest.class);
when(request.getHeader("Authorization")).thenReturn("Basic credentials");
HttpServletResponse response = mock(HttpServletResponse.class);
FilterChain chain = mock(FilterChain.class);
filter.doFilter(request, response, chain);
assertNull(SecurityContextHolder.getContext().getAuthentication());
verify(chain).doFilter(request, response);
verify(jwtConfig, never()).validateToken(any());
}
@Test
void filterCallsDoFilterOnChain() throws Exception {
OncePerRequestFilter filter = getJwtFilter();
var request = mock(HttpServletRequest.class);
var response = mock(HttpServletResponse.class);
var chain = mock(FilterChain.class);
filter.doFilter(request, response, chain);
verify(chain).doFilter(request, response);
}
@Test
void alwaysCallsDoFilterEvenOnException() throws Exception {
OncePerRequestFilter filter = getJwtFilter();
var request = mock(HttpServletRequest.class);
when(request.getHeader("Authorization")).thenReturn("Bearer token");
when(jwtConfig.validateToken(anyString())).thenThrow(new RuntimeException("fail"));
var response = mock(HttpServletResponse.class);
var chain = mock(FilterChain.class);
filter.doFilter(request, response, chain);
verify(chain).doFilter(request, response);
}
private OncePerRequestFilter getJwtFilter() throws Exception {
Method method = SecurityConfig.class.getDeclaredMethod("jwtAuthenticationFilter");
method.setAccessible(true);
return (OncePerRequestFilter) method.invoke(securityConfig);
}
}

View File

@@ -0,0 +1,30 @@
package com.krrishg.config;
import org.junit.jupiter.api.Test;
import software.amazon.awssdk.services.s3.S3Client;
import java.lang.reflect.Field;
import static org.junit.jupiter.api.Assertions.*;
class StorageConfigTest {
@Test
void s3ClientIsCreatedWithConfiguration() throws Exception {
StorageConfig config = new StorageConfig();
setField(config, "endpoint", "http://localhost:9000");
setField(config, "region", "us-east-1");
setField(config, "accessKey", "test-access-key");
setField(config, "secretKey", "test-secret-key");
S3Client client = config.s3Client();
assertNotNull(client);
client.close();
}
private void setField(Object target, String name, Object value) throws Exception {
Field field = target.getClass().getDeclaredField(name);
field.setAccessible(true);
field.set(target, value);
}
}

View File

@@ -0,0 +1,165 @@
package com.krrishg.service;
import com.krrishg.model.User;
import com.krrishg.model.VerificationToken;
import com.krrishg.support.FakeUserRepository;
import com.krrishg.support.FakeVerificationTokenRepository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.time.LocalDateTime;
import static org.junit.jupiter.api.Assertions.*;
class CleanupServiceTest {
private FakeUserRepository userRepository;
private FakeVerificationTokenRepository tokenRepository;
private CleanupService cleanupService;
@BeforeEach
void setUp() {
userRepository = new FakeUserRepository();
tokenRepository = new FakeVerificationTokenRepository();
cleanupService = new CleanupService(userRepository, tokenRepository);
}
@Test
void deletesUnverifiedUserWithExpiredToken() {
User user = User.builder()
.email("stale@example.com")
.emailVerified(false)
.build();
user.onCreate();
userRepository.save(user);
VerificationToken token = VerificationToken.builder()
.userId(user.getId())
.token("expired-token")
.used(false)
.expiryDate(LocalDateTime.now().minusDays(3))
.build();
tokenRepository.save(token);
cleanupService.cleanupStaleUnverifiedAccounts();
assertTrue(userRepository.findById(user.getId()).isEmpty());
assertTrue(tokenRepository.findByToken("expired-token").isEmpty());
}
@Test
void preservesVerifiedUserWithExpiredToken() {
User user = User.builder()
.email("verified@example.com")
.emailVerified(true)
.build();
user.onCreate();
userRepository.save(user);
VerificationToken token = VerificationToken.builder()
.userId(user.getId())
.token("verified-expired")
.used(false)
.expiryDate(LocalDateTime.now().minusDays(3))
.build();
tokenRepository.save(token);
cleanupService.cleanupStaleUnverifiedAccounts();
assertTrue(userRepository.findById(user.getId()).isPresent());
assertTrue(tokenRepository.findByToken("verified-expired").isPresent());
}
@Test
void preservesUnverifiedUserWithValidToken() {
User user = User.builder()
.email("pending@example.com")
.emailVerified(false)
.build();
user.onCreate();
userRepository.save(user);
VerificationToken token = VerificationToken.builder()
.userId(user.getId())
.token("valid-token")
.used(false)
.expiryDate(LocalDateTime.now().plusDays(1))
.build();
tokenRepository.save(token);
cleanupService.cleanupStaleUnverifiedAccounts();
assertTrue(userRepository.findById(user.getId()).isPresent());
assertTrue(tokenRepository.findByToken("valid-token").isPresent());
}
@Test
void doesNothingWhenNoStaleTokens() {
cleanupService.cleanupStaleUnverifiedAccounts();
assertEquals(0, userRepository.count());
assertEquals(0, tokenRepository.count());
}
@Test
void cleansUpMultipleStaleUsers() {
User user1 = User.builder()
.email("stale1@example.com")
.emailVerified(false)
.build();
user1.onCreate();
userRepository.save(user1);
User user2 = User.builder()
.email("stale2@example.com")
.emailVerified(false)
.build();
user2.onCreate();
userRepository.save(user2);
VerificationToken token1 = VerificationToken.builder()
.userId(user1.getId())
.token("stale-token-1")
.used(false)
.expiryDate(LocalDateTime.now().minusDays(3))
.build();
tokenRepository.save(token1);
VerificationToken token2 = VerificationToken.builder()
.userId(user2.getId())
.token("stale-token-2")
.used(false)
.expiryDate(LocalDateTime.now().minusDays(3))
.build();
tokenRepository.save(token2);
cleanupService.cleanupStaleUnverifiedAccounts();
assertTrue(userRepository.findById(user1.getId()).isEmpty());
assertTrue(userRepository.findById(user2.getId()).isEmpty());
assertTrue(tokenRepository.findByToken("stale-token-1").isEmpty());
assertTrue(tokenRepository.findByToken("stale-token-2").isEmpty());
}
@Test
void skipsUsedToken() {
User user = User.builder()
.email("used-token@example.com")
.emailVerified(false)
.build();
user.onCreate();
userRepository.save(user);
VerificationToken token = VerificationToken.builder()
.userId(user.getId())
.token("used-token")
.used(true)
.expiryDate(LocalDateTime.now().minusDays(3))
.build();
tokenRepository.save(token);
cleanupService.cleanupStaleUnverifiedAccounts();
assertTrue(userRepository.findById(user.getId()).isPresent());
}
}

View File

@@ -0,0 +1,83 @@
package com.krrishg.service;
import jakarta.mail.Session;
import jakarta.mail.internet.MimeMessage;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.mail.javamail.JavaMailSender;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Properties;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
class EmailServiceTest {
@Mock
private JavaMailSender mailSender;
private EmailService emailService;
@BeforeEach
void setUp() {
emailService = new EmailService(mailSender, "http://localhost:4200", "noreply@indie.app");
}
@Test
void sendsEmailWithCorrectRecipientsAndSubject() throws Exception {
MimeMessage message = new MimeMessage(Session.getDefaultInstance(new Properties()));
when(mailSender.createMimeMessage()).thenReturn(message);
emailService.sendVerificationEmail("user@example.com", "User", "token123");
verify(mailSender).send(message);
String raw = writeMessage(message);
assertTrue(raw.contains("From: noreply@indie.app"));
assertTrue(raw.contains("To: user@example.com"));
assertTrue(raw.contains("Subject: Verify your Indie account"));
}
@Test
void emailBodyContainsVerificationLink() throws Exception {
MimeMessage message = new MimeMessage(Session.getDefaultInstance(new Properties()));
when(mailSender.createMimeMessage()).thenReturn(message);
emailService.sendVerificationEmail("user@example.com", "User", "test-token-123");
String raw = writeMessage(message);
assertTrue(raw.contains("http://localhost:4200/verify-email?token=test-token-123"));
assertTrue(raw.contains("Hi User,"));
}
@Test
void usesEmailPrefixWhenNameIsNull() throws Exception {
MimeMessage message = new MimeMessage(Session.getDefaultInstance(new Properties()));
when(mailSender.createMimeMessage()).thenReturn(message);
emailService.sendVerificationEmail("john.doe@example.com", null, "token123");
String raw = writeMessage(message);
assertTrue(raw.contains("Hi john.doe,"));
}
@Test
void propagatesExceptionFromMailSender() {
when(mailSender.createMimeMessage()).thenThrow(new RuntimeException("mail error"));
RuntimeException ex = assertThrows(RuntimeException.class,
() -> emailService.sendVerificationEmail("user@example.com", "User", "token123"));
assertEquals("mail error", ex.getMessage());
}
private String writeMessage(MimeMessage message) throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
message.writeTo(baos);
return baos.toString(StandardCharsets.UTF_8);
}
}

View File

@@ -1,13 +1,19 @@
package com.krrishg.service; package com.krrishg.service;
import com.krrishg.model.SelfHostedConfig; import com.krrishg.model.SelfHostedConfig;
import com.krrishg.service.RenderService.SiteOutput;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import java.io.IOException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Comparator;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*;
@@ -48,6 +54,30 @@ class SshRsyncDeployerTest {
assertEquals("https://example.com/", url); assertEquals("https://example.com/", url);
} }
@Test
void buildUrlHandlesDomainWithTrailingSlash() {
SelfHostedConfig config = SelfHostedConfig.builder()
.domain("example.com")
.sslEnabled(true)
.build();
String url = deployer.buildUrl(config);
assertEquals("https://example.com/", url);
}
@Test
void buildUrlHandlesIpAddress() {
SelfHostedConfig config = SelfHostedConfig.builder()
.domain("192.168.1.100")
.sslEnabled(false)
.build();
String url = deployer.buildUrl(config);
assertEquals("http://192.168.1.100/", url);
}
@Test @Test
void buildNginxConfigWithSslIncludesRedirect() throws Exception { void buildNginxConfigWithSslIncludesRedirect() throws Exception {
Method method = SshRsyncDeployer.class.getDeclaredMethod( Method method = SshRsyncDeployer.class.getDeclaredMethod(
@@ -76,4 +106,70 @@ class SshRsyncDeployerTest {
assertFalse(config.contains("ssl_certificate")); assertFalse(config.contains("ssl_certificate"));
assertFalse(config.contains("return 301")); assertFalse(config.contains("return 301"));
} }
@Test
void buildNginxConfigWithoutSslHasNoSslRedirect() throws Exception {
Method method = SshRsyncDeployer.class.getDeclaredMethod(
"buildNginxConfig", String.class, String.class, boolean.class);
method.setAccessible(true);
String config = (String) method.invoke(deployer, "example.com", "/var/www/public", false);
assertFalse(config.contains("return 301"));
assertFalse(config.contains("ssl_certificate"));
assertFalse(config.contains("listen 443"));
}
@Test
void provisionSslThrowsWhenSslEmailIsNull() throws Exception {
Method method = SshRsyncDeployer.class.getDeclaredMethod(
"provisionSsl", String.class, SelfHostedConfig.class, String.class);
method.setAccessible(true);
SelfHostedConfig config = SelfHostedConfig.builder()
.domain("example.com")
.sslEmail(null)
.build();
var thrown = assertThrows(Exception.class,
() -> method.invoke(deployer, "/path/key", config, "/var/www/public"));
assertTrue(thrown.getCause().getMessage().contains("SSL email is required"));
}
@Test
void writeSiteFilesCreatesDirectoryStructure() throws Exception {
Method method = SshRsyncDeployer.class.getDeclaredMethod(
"writeSiteFiles", Path.class, SiteOutput.class);
method.setAccessible(true);
Path tempDir = Files.createTempDirectory("test-deploy");
try {
Map<String, String> pages = Map.of(
"home", "<h1>Home</h1>",
"about", "<h1>About</h1>"
);
SiteOutput output = new SiteOutput("body {}", "console.log('hi')", pages);
method.invoke(deployer, tempDir, output);
Path siteDir = tempDir.resolve("site");
assertTrue(Files.exists(siteDir.resolve("index.html")));
assertTrue(Files.exists(siteDir.resolve("style.css")));
assertTrue(Files.exists(siteDir.resolve("script.js")));
assertTrue(Files.exists(siteDir.resolve("about/index.html")));
assertTrue(Files.exists(siteDir.resolve("404.html")));
assertEquals("<h1>Home</h1>", Files.readString(siteDir.resolve("index.html")));
assertEquals("<h1>About</h1>", Files.readString(siteDir.resolve("about/index.html")));
} finally {
try (var walk = Files.walk(tempDir)) {
walk.sorted(Comparator.reverseOrder())
.forEach(p -> {
try {
Files.deleteIfExists(p);
} catch (IOException e) {
}
});
}
}
}
} }