Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.sql.SQLException;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;

import static org.junit.jupiter.api.Assertions.assertEquals;
Expand Down Expand Up @@ -260,6 +261,134 @@ protected boolean blockMainThreadOnStart() {
}
}

@Test
@DisplayName("Given a non-blocking database, when initializeDatabase is called, then tables are created asynchronously")
void initializeDatabase_createsTablesAsync_withNonBlockingStartup() throws Exception {
setupDriverRegistry();

NonBlockingTestDatabase database = new NonBlockingTestDatabase(plugin);
try {
database.initializeDatabase();

// Wait for async chain to complete
awaitDatabaseExecutor(database);

Connection conn = database.getConnection();
assertTrue(database.tableExists(conn, "table_history"));
conn.close();
} finally {
database.shutdown();
}
}

@Test
@DisplayName("Given a non-blocking database with custom functions, when initializeDatabase is called, then custom create and update functions are invoked asynchronously")
void initializeDatabase_invokesCustomFunctionsAsync_withNonBlockingStartup() throws Exception {
setupDriverRegistry();

NonBlockingTestDatabase database = new NonBlockingTestDatabase(plugin);
boolean[] createCalled = {false};
boolean[] updateCalled = {false};

database.addCreateTableFunction(db -> {
CompletableFuture<Void> future = new CompletableFuture<>();
db.getDatabaseExecutorService().submit(() -> {
createCalled[0] = true;
future.complete(null);
});
return future;
});
database.addUpdateTableFunction(db -> {
CompletableFuture<Void> future = new CompletableFuture<>();
db.getDatabaseExecutorService().submit(() -> {
updateCalled[0] = true;
future.complete(null);
});
return future;
});

try {
database.initializeDatabase();

awaitDatabaseExecutor(database);

assertTrue(createCalled[0]);
assertTrue(updateCalled[0]);
} finally {
database.shutdown();
}
}

@Test
@DisplayName("Given a non-blocking database with multiple custom functions, when initializeDatabase is called, then all functions are invoked")
void initializeDatabase_invokesAllCustomFunctionsAsync_withNonBlockingStartup() throws Exception {
setupDriverRegistry();

NonBlockingTestDatabase database = new NonBlockingTestDatabase(plugin);
boolean[] create1Called = {false};
boolean[] create2Called = {false};
boolean[] update1Called = {false};
boolean[] update2Called = {false};

database.addCreateTableFunction(db -> {
CompletableFuture<Void> future = new CompletableFuture<>();
db.getDatabaseExecutorService().submit(() -> {
create1Called[0] = true;
future.complete(null);
});
return future;
});
database.addCreateTableFunction(db -> {
CompletableFuture<Void> future = new CompletableFuture<>();
db.getDatabaseExecutorService().submit(() -> {
create2Called[0] = true;
future.complete(null);
});
return future;
});
database.addUpdateTableFunction(db -> {
CompletableFuture<Void> future = new CompletableFuture<>();
db.getDatabaseExecutorService().submit(() -> {
update1Called[0] = true;
future.complete(null);
});
return future;
});
database.addUpdateTableFunction(db -> {
CompletableFuture<Void> future = new CompletableFuture<>();
db.getDatabaseExecutorService().submit(() -> {
update2Called[0] = true;
future.complete(null);
});
return future;
});

try {
database.initializeDatabase();

awaitDatabaseExecutor(database);

assertTrue(create1Called[0]);
assertTrue(create2Called[0]);
assertTrue(update1Called[0]);
assertTrue(update2Called[0]);
} finally {
database.shutdown();
}
}

private static void awaitDatabaseExecutor(Database database) throws InterruptedException {
CompletableFuture<Void> sentinel = new CompletableFuture<>();
database.getDatabaseExecutorService().submit(() -> sentinel.complete(null));
try {
sentinel.get(10, TimeUnit.SECONDS);
} catch (java.util.concurrent.ExecutionException | java.util.concurrent.TimeoutException e) {
throw new RuntimeException(e);
}
// Allow async thenAccept callbacks to propagate
Thread.sleep(500);
}

static class LeakDetectionTestDatabase extends Database {
private static final Credentials CREDENTIALS = new Credentials("", 0, "", "", "");
private static final ConnectionDetails CONNECTION_DETAILS = new ConnectionDetails(5000, 300000, 600000, 2, 10, 30000);
Expand Down
Loading
Loading