Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up JDK 23
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
Expand Down
1 change: 1 addition & 0 deletions frontendtest/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<html><body><h1>Welcome, {{username}}</h1></body></html>
1 change: 1 addition & 0 deletions frontendtest/login.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<html><body>Login Page</body></html>
20 changes: 20 additions & 0 deletions frontendtest/register.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<!DOCTYPE html>
<html>
<head>
<title>Register</title>
</head>
<body>
<h2>Register</h2>
<form action="http://localhost:8080/register" method="post">
<label>Username:</label><br>
<input type="text" name="username" required><br><br>

<label>Password:</label><br>
<input type="password" name="password" required><br><br>

<input type="submit" value="Register">
</form>

<p>Already have an account? <a href="login.html">Login here</a></p>
</body>
</html>
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
<configuration>
<source>23</source>
<target>23</target>
<source>17</source>
<target>17</target>
</configuration>
</plugin>

Expand Down
5 changes: 2 additions & 3 deletions src/main/java/urlshortener/Server.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,15 @@ public static void main(String[] args) throws Exception {
// Setup H2 database in memory
conn = DriverManager.getConnection(AppConfig.DB_URL, AppConfig.DB_USER, AppConfig.DB_PASS);
try (Statement stmt = conn.createStatement()) {
stmt.execute("CREATE TABLE users (username VARCHAR(255) PRIMARY KEY, password VARCHAR(255))");
}
stmt.execute("DROP TABLE IF EXISTS users");
stmt.execute("CREATE TABLE users (username VARCHAR(255) PRIMARY KEY, password VARCHAR(255))"); }

HttpServer server = HttpServer.create(new InetSocketAddress(AppConfig.PORT), 0);
server.createContext("/register", new RegisterHandler(getConn()));
server.createContext("/login", new LoginHandler(getConn()));
server.createContext("/index", new HomePageHandler(getConn()));
server.createContext("/", new HomePageHandler(getConn()));
server.createContext("/shorten", new UrlHandler());
server.createContext("/s", new RedirectHandler());
server.setExecutor(null);
server.start();

Expand Down
9 changes: 6 additions & 3 deletions src/main/java/urlshortener/database/Database.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package urlshortener.database;

import lombok.Setter;

import java.sql.*;

public class Database {
private static final String JDBC_URL = "jdbc:h2:./shorten-db";
@Setter
private static String jdbcUrl = "jdbc:h2:./shorten-db"; // default

static {
public static void initSchema() {
try (Connection conn = getConnection(); Statement stmt = conn.createStatement()) {
stmt.executeUpdate("CREATE TABLE IF NOT EXISTS urls (" +
"id IDENTITY PRIMARY KEY, " +
Expand All @@ -17,7 +20,7 @@ public class Database {
}

public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(JDBC_URL, "sa", "");
return DriverManager.getConnection(jdbcUrl, "sa", "");
}

public static void insertUrl(String shortCode, String longUrl) throws SQLException {
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/urlshortener/service/HomePageHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

public class HomePageHandler implements HttpHandler {
private static Connection conn;
String fileLocation = "frontend/index.html";

public HomePageHandler(Connection conn) {
HomePageHandler.conn = conn;
Expand All @@ -32,8 +33,7 @@ public void handle(HttpExchange exchange) throws IOException {
exchange.sendResponseHeaders(405, -1);
return;
}

String html = new String(Files.readAllBytes(Paths.get("frontend/index.html")));
String html = new String(Files.readAllBytes(Paths.get(fileLocation)));
html = html.replace("{{username}}", username);

byte[] response = html.getBytes();
Expand Down
71 changes: 71 additions & 0 deletions src/test/java/urlshortener/FakeHttpExchange.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package urlshortener;

import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpContext;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpPrincipal;
import lombok.Getter;

import java.io.*;
import java.net.InetSocketAddress;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.*;

public class FakeHttpExchange extends HttpExchange {
private final ByteArrayOutputStream responseStream = new ByteArrayOutputStream();
private final Headers requestHeaders = new Headers();
private final Headers responseHeaders = new Headers();
private final URI uri;
private final String method;
private ByteArrayOutputStream responseBody = new ByteArrayOutputStream();
private InputStream requestBody = InputStream.nullInputStream();
@Getter
private int statusCode;


public FakeHttpExchange(String method, String uri) {
this.method = method;
this.uri = URI.create(uri);
}

@Override public Headers getRequestHeaders() { return requestHeaders; }
@Override public Headers getResponseHeaders() { return responseHeaders; }
@Override public URI getRequestURI() { return uri; }
@Override public String getRequestMethod() { return method; }
@Override public OutputStream getResponseBody() { return responseStream; }
@Override public InputStream getRequestBody() { return InputStream.nullInputStream(); }

@Override public HttpContext getHttpContext() { return null; }
@Override public void close() {}
@Override public InetSocketAddress getRemoteAddress() { return null; }

public String getResponseText() {
return responseBody.toString(StandardCharsets.UTF_8);
}

@Override
public int getResponseCode() {
return 0;
}

@Override
public void sendResponseHeaders(int code, long length) {
this.statusCode = code;
}

@Override public InetSocketAddress getLocalAddress() { return null; }
@Override public String getProtocol() { return null; }
@Override public Object getAttribute(String s) { return null; }
@Override public void setAttribute(String s, Object o) {}
@Override public void setStreams(InputStream inputStream, OutputStream outputStream) {}

@Override
public HttpPrincipal getPrincipal() {
return null;
}

public void setRequestBody(String body) {
this.requestBody = new ByteArrayInputStream(body.getBytes(StandardCharsets.UTF_8));
}
}
8 changes: 4 additions & 4 deletions src/test/java/urlshortener/ServerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@ static void setupDatabase() throws Exception {
// Use real in-memory H2 database for integration-style test
conn = DriverManager.getConnection("jdbc:h2:mem:testdb", "sa", "");
try (Statement stmt = conn.createStatement()) {
stmt.execute("CREATE TABLE users (username VARCHAR(255) PRIMARY KEY, password VARCHAR(255))");
stmt.execute("CREATE TABLE userstest (username VARCHAR(255) PRIMARY KEY, password VARCHAR(255))");
}
}

@Test
void testUsersTableCreatedSuccessfully() throws Exception {
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'USERS'");
assertTrue(rs.next(), "Users table should exist");
assertTrue(rs.next(), "Userstest table should exist");
}

@Test
Expand All @@ -40,11 +40,11 @@ void testMockedConnectionTableCreation() throws Exception {
when(mockStmt.execute(anyString())).thenReturn(true);

// Run the DB setup logic
mockConn.createStatement().execute("CREATE TABLE users (username VARCHAR(255) PRIMARY KEY, password VARCHAR(255))");
mockConn.createStatement().execute("CREATE TABLE userstest (username VARCHAR(255) PRIMARY KEY, password VARCHAR(255))");

// Verify SQL execution
verify(mockConn).createStatement();
verify(mockStmt).execute("CREATE TABLE users (username VARCHAR(255) PRIMARY KEY, password VARCHAR(255))");
verify(mockStmt).execute("CREATE TABLE userstest (username VARCHAR(255) PRIMARY KEY, password VARCHAR(255))");
}

@Test
Expand Down
76 changes: 76 additions & 0 deletions src/test/java/urlshortener/service/HomePageHandlerTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package urlshortener.service;


import org.junit.jupiter.api.*;
import urlshortener.FakeHttpExchange;
import urlshortener.config.AppConfig;
import urlshortener.service.HomePageHandler;

import java.io.ByteArrayOutputStream;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

import static java.nio.charset.StandardCharsets.UTF_8;

class HomePageHandlerTest {

Connection conn = DriverManager.getConnection("jdbc:h2:mem:testdb", "sa", "");

private static final String HTML_TEMPLATE =
"<html><body><h1>Welcome, {{username}}</h1></body></html>";
private static final String INDEX_PATH = "frontendtest/index.html";

HomePageHandlerTest() throws SQLException {
}

@BeforeAll
static void setup() throws Exception {
Files.createDirectories(Paths.get("frontendtest"));
try (PrintWriter writer = new PrintWriter(new FileWriter(INDEX_PATH))) {
writer.write(HTML_TEMPLATE);
}
}

// @AfterAll
// static void cleanup() throws Exception {
// Files.deleteIfExists(Paths.get(INDEX_PATH));
// Files.deleteIfExists(Paths.get("frontendtest"));
// }

@Test
void testHomePageHandlerWithUsernameQuery() throws Exception {
FakeHttpExchange exchange = new FakeHttpExchange("GET", "/?username=sa");
HomePageHandler handler = new HomePageHandler(conn);
handler.fileLocation = INDEX_PATH;

handler.handle(exchange);

// Properly read the actual HTML string from the output stream
String response = convertStreamToString((ByteArrayOutputStream) exchange.getResponseBody()); // custom method that does .toString(StandardCharsets.UTF_8)
Assertions.assertTrue(response.contains("Welcome, sa"));
}

@Test
void testHomePageHandlerWithoutQuery() throws Exception {
FakeHttpExchange exchange = new FakeHttpExchange("GET", "/");
HomePageHandler handler = new HomePageHandler(conn);
handler.fileLocation = INDEX_PATH;

handler.handle(exchange);

String response = convertStreamToString((ByteArrayOutputStream) exchange.getResponseBody()); // custom method that does .toString(StandardCharsets.UTF_8)
Assertions.assertFalse(response.contains("Welcome, sa"));
}

public String convertStreamToString(ByteArrayOutputStream outputStream) {
if (outputStream == null) {
return "";
}
return outputStream.toString(StandardCharsets.UTF_8);
}
}
29 changes: 29 additions & 0 deletions src/test/java/urlshortener/service/UrlHandlerTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package urlshortener.service;

import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import urlshortener.database.Database;

import static org.junit.jupiter.api.Assertions.*;

public class UrlHandlerTest {

@BeforeAll
static void setupDatabase() {
// Use in-memory DB for isolated testing
Database.setJdbcUrl("jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1");
Database.initSchema(); // Create `urls` table
}

@Test
void testInsertAndRetrieveUrl() throws Exception {
String shortCode = "abc123";
String longUrl = "https://example.com";

Database.insertUrl(shortCode, longUrl);
String fetched = Database.getLongUrl(shortCode);

assertEquals(longUrl, fetched);
}

}