diff --git a/bindings/java/Makefile b/bindings/java/Makefile index e91fcc923..a8e979dbc 100644 --- a/bindings/java/Makefile +++ b/bindings/java/Makefile @@ -1,7 +1,7 @@ -java_run: lib - export LIMBO_SYSTEM_PATH=../../target/debug && ./gradlew run - .PHONY: lib -lib: - cargo build +run_test: build_test + ./gradlew test + +build_test: + CARGO_TARGET_DIR=src/test/resources/limbo cargo build diff --git a/bindings/java/build.gradle.kts b/bindings/java/build.gradle.kts index 331b4831f..f1349859d 100644 --- a/bindings/java/build.gradle.kts +++ b/bindings/java/build.gradle.kts @@ -13,6 +13,7 @@ repositories { dependencies { testImplementation(platform("org.junit:junit-bom:5.10.0")) testImplementation("org.junit.jupiter:junit-jupiter") + testImplementation("org.assertj:assertj-core:3.27.0") } application { @@ -28,4 +29,6 @@ application { tasks.test { useJUnitPlatform() + // In order to find rust built file under resources, we need to set it as system path + systemProperty("java.library.path", "${System.getProperty("java.library.path")}:$projectDir/src/test/resources/limbo/debug") } diff --git a/bindings/java/rs_src/errors.rs b/bindings/java/rs_src/errors.rs index 490fdfbd9..7924ffb68 100644 --- a/bindings/java/rs_src/errors.rs +++ b/bindings/java/rs_src/errors.rs @@ -6,7 +6,7 @@ pub struct CustomError { } /// This struct defines error codes that correspond to the constants defined in the -/// Java package `org.github.tursodatabase.exceptions.ErrorCode`. +/// Java package `org.github.tursodatabase.LimboErrorCode`. /// /// These error codes are used to handle and represent specific error conditions /// that may occur within the Rust code and need to be communicated to the Java side. @@ -14,8 +14,7 @@ pub struct CustomError { pub struct ErrorCode; impl ErrorCode { - pub const CONNECTION_FAILURE: i32 = -1; - + // TODO: change CONNECTION_FAILURE_STATEMENT_IS_DML to appropriate error code number pub const STATEMENT_IS_DML: i32 = -1; } diff --git a/bindings/java/rs_src/lib.rs b/bindings/java/rs_src/lib.rs index 4ba9ba2c4..4dfadd743 100644 --- a/bindings/java/rs_src/lib.rs +++ b/bindings/java/rs_src/lib.rs @@ -1,66 +1,6 @@ mod connection; mod cursor; mod errors; +mod limbo_db; mod macros; mod utils; - -use crate::connection::Connection; -use crate::errors::ErrorCode; -use jni::errors::JniError; -use jni::objects::{JClass, JString}; -use jni::sys::jlong; -use jni::JNIEnv; -use std::sync::{Arc, Mutex}; - -/// Establishes a connection to the database specified by the given path. -/// -/// This function is called from the Java side to create a connection to the database. -/// It returns a pointer to the `Connection` object, which can be used in subsequent -/// native function calls. -/// -/// # Arguments -/// -/// * `env` - The JNI environment pointer. -/// * `_class` - The Java class calling this function. -/// * `path` - A `JString` representing the path to the database file. -/// -/// # Returns -/// -/// A `jlong` representing the pointer to the newly created `Connection` object, -/// or [ErrorCode::CONNECTION_FAILURE] if the connection could not be established. -#[no_mangle] -pub extern "system" fn Java_org_github_tursodatabase_limbo_Limbo_connect<'local>( - mut env: JNIEnv<'local>, - _class: JClass<'local>, - path: JString<'local>, -) -> jlong { - connect_internal(&mut env, path).unwrap_or_else(|_| ErrorCode::CONNECTION_FAILURE as jlong) -} - -#[allow(improper_ctypes_definitions, clippy::arc_with_non_send_sync)] // TODO: remove -fn connect_internal<'local>( - env: &mut JNIEnv<'local>, - path: JString<'local>, -) -> Result { - let io = Arc::new(limbo_core::PlatformIO::new().map_err(|e| { - println!("IO initialization failed: {:?}", e); - JniError::Unknown - })?); - - let path: String = env - .get_string(&path) - .expect("Failed to convert JString to Rust String") - .into(); - let db = limbo_core::Database::open_file(io.clone(), &path).map_err(|e| { - println!("Failed to open database: {:?}", e); - JniError::Unknown - })?; - - let conn = db.connect().clone(); - let connection = Connection { - conn: Arc::new(Mutex::new(conn)), - io, - }; - - Ok(Box::into_raw(Box::new(connection)) as jlong) -} diff --git a/bindings/java/rs_src/limbo_db.rs b/bindings/java/rs_src/limbo_db.rs new file mode 100644 index 000000000..4357b7a8a --- /dev/null +++ b/bindings/java/rs_src/limbo_db.rs @@ -0,0 +1,86 @@ +use jni::objects::{JByteArray, JObject}; +use jni::sys::{jint, jlong}; +use jni::JNIEnv; +use limbo_core::Database; +use std::sync::Arc; + +const ERROR_CODE_ETC: i32 = 9999; + +#[no_mangle] +#[allow(clippy::arc_with_non_send_sync)] +pub extern "system" fn Java_org_github_tursodatabase_core_LimboDB__1open_1utf8<'local>( + mut env: JNIEnv<'local>, + obj: JObject<'local>, + file_name_byte_arr: JByteArray<'local>, + _open_flags: jint, +) -> jlong { + let io = match limbo_core::PlatformIO::new() { + Ok(io) => Arc::new(io), + Err(e) => { + set_err_msg_and_throw_exception(&mut env, obj, ERROR_CODE_ETC, e.to_string()); + return -1; + } + }; + + let path = match env + .convert_byte_array(file_name_byte_arr) + .map_err(|e| e.to_string()) + { + Ok(bytes) => match String::from_utf8(bytes) { + Ok(s) => s, + Err(e) => { + set_err_msg_and_throw_exception(&mut env, obj, ERROR_CODE_ETC, e.to_string()); + return -1; + } + }, + Err(e) => { + set_err_msg_and_throw_exception(&mut env, obj, ERROR_CODE_ETC, e.to_string()); + return -1; + } + }; + + let db = match Database::open_file(io.clone(), &path) { + Ok(db) => db, + Err(e) => { + set_err_msg_and_throw_exception(&mut env, obj, ERROR_CODE_ETC, e.to_string()); + return -1; + } + }; + + Box::into_raw(Box::new(db)) as jlong +} + +fn set_err_msg_and_throw_exception<'local>( + env: &mut JNIEnv<'local>, + obj: JObject<'local>, + err_code: i32, + err_msg: String, +) { + let error_message_pointer = Box::into_raw(Box::new(err_msg)) as i64; + match env.call_method( + obj, + "newSQLException", + "(IJ)Lorg/github/tursodatabase/exceptions/LimboException;", + &[err_code.into(), error_message_pointer.into()], + ) { + Ok(_) => { + // do nothing because above method will always return Err + } + Err(_e) => { + // do nothing because our java app will handle Err + } + } +} + +#[no_mangle] +pub unsafe extern "system" fn Java_org_github_tursodatabase_core_LimboDB_getErrorMessageUtf8< + 'local, +>( + env: JNIEnv<'local>, + _obj: JObject<'local>, + error_message_ptr: jlong, +) -> JByteArray<'local> { + let error_message = Box::from_raw(error_message_ptr as *mut String); + let error_message_bytes = error_message.as_bytes(); + env.byte_array_from_slice(error_message_bytes).unwrap() +} diff --git a/bindings/java/src/main/java/org/github/tursodatabase/LimboErrorCode.java b/bindings/java/src/main/java/org/github/tursodatabase/LimboErrorCode.java new file mode 100644 index 000000000..0c65ba04f --- /dev/null +++ b/bindings/java/src/main/java/org/github/tursodatabase/LimboErrorCode.java @@ -0,0 +1,34 @@ +package org.github.tursodatabase; + +public enum LimboErrorCode { + UNKNOWN_ERROR(-1, "Unknown error"), + ETC(9999, "Unclassified error"); + + public final int code; + public final String message; + + /** + * @param code Error code + * @param message Message for the error. + */ + LimboErrorCode(int code, String message) { + this.code = code; + this.message = message; + } + + public static LimboErrorCode getErrorCode(int errorCode) { + for (LimboErrorCode limboErrorCode: LimboErrorCode.values()) { + if (errorCode == limboErrorCode.code) return limboErrorCode; + } + + return UNKNOWN_ERROR; + } + + @Override + public String toString() { + return "LimboErrorCode{" + + "code=" + code + + ", message='" + message + '\'' + + '}'; + } +} diff --git a/bindings/java/src/main/java/org/github/tursodatabase/Main.java b/bindings/java/src/main/java/org/github/tursodatabase/Main.java deleted file mode 100644 index de9b94e36..000000000 --- a/bindings/java/src/main/java/org/github/tursodatabase/Main.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.github.tursodatabase; - -import org.github.tursodatabase.limbo.Connection; -import org.github.tursodatabase.limbo.Cursor; -import org.github.tursodatabase.limbo.Limbo; - -/** - * TODO: Remove Main class. We can use test code to verify behaviors. - */ -public class Main { - public static void main(String[] args) throws Exception { - Limbo limbo = Limbo.create(); - Connection connection = limbo.getConnection("database.db"); - - Cursor cursor = connection.cursor(); - cursor.execute("SELECT * FROM example_table;"); - System.out.println("result: " + cursor.fetchOne()); - } -} diff --git a/bindings/java/src/main/java/org/github/tursodatabase/NativeInvocation.java b/bindings/java/src/main/java/org/github/tursodatabase/NativeInvocation.java new file mode 100644 index 000000000..ee91caf53 --- /dev/null +++ b/bindings/java/src/main/java/org/github/tursodatabase/NativeInvocation.java @@ -0,0 +1,15 @@ +package org.github.tursodatabase; + + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation to mark methods that are called by native functions. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface NativeInvocation { +} diff --git a/bindings/java/src/main/java/org/github/tursodatabase/core/DB.java b/bindings/java/src/main/java/org/github/tursodatabase/core/AbstractDB.java similarity index 73% rename from bindings/java/src/main/java/org/github/tursodatabase/core/DB.java rename to bindings/java/src/main/java/org/github/tursodatabase/core/AbstractDB.java index 4d82a7a92..adea4cb20 100644 --- a/bindings/java/src/main/java/org/github/tursodatabase/core/DB.java +++ b/bindings/java/src/main/java/org/github/tursodatabase/core/AbstractDB.java @@ -1,5 +1,9 @@ package org.github.tursodatabase.core; +import org.github.tursodatabase.LimboErrorCode; +import org.github.tursodatabase.NativeInvocation; +import org.github.tursodatabase.exceptions.LimboException; + import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; import java.util.concurrent.atomic.AtomicBoolean; @@ -10,18 +14,14 @@ import java.util.concurrent.atomic.AtomicBoolean; * are not only to provide functionality, but to handle contractual * differences between the JDBC specification and the Limbo API. */ -public abstract class DB { +public abstract class AbstractDB { private final String url; private final String fileName; private final AtomicBoolean closed = new AtomicBoolean(true); - public DB(String url, String fileName) throws SQLException { + public AbstractDB(String url, String filaName) throws SQLException { this.url = url; - this.fileName = fileName; - } - - public String getUrl() { - return url; + this.fileName = filaName; } public boolean isClosed() { @@ -36,7 +36,7 @@ public abstract class DB { /** * Executes an SQL statement. * - * @param sql SQL statement to be executed. + * @param sql SQL statement to be executed. * @param autoCommit Whether to auto-commit the transaction. * @throws SQLException if a database access error occurs. */ @@ -47,17 +47,16 @@ public abstract class DB { /** * Creates an SQLite interface to a database for the given connection. - * @see SQLite Open Flags * - * @param fileName The database. * @param openFlags Flags for opening the database. * @throws SQLException if a database access error occurs. */ - public final synchronized void open(String fileName, int openFlags) throws SQLException { - // TODO: add implementation - throw new SQLFeatureNotSupportedException(); + public final synchronized void open(int openFlags) throws SQLException { + _open(fileName, openFlags); } + protected abstract void _open(String fileName, int openFlags) throws SQLException; + /** * Closes a database connection and finalizes any remaining statements before the closing * operation. @@ -95,13 +94,13 @@ public abstract class DB { /** * Creates an SQLite interface to a database with the provided open flags. - * @see SQLite Open Flags * - * @param filename The database to open. + * @param fileName The database to open. * @param openFlags Flags for opening the database. + * @return pointer to database instance * @throws SQLException if a database access error occurs. */ - protected abstract void _open(String filename, int openFlags) throws SQLException; + protected abstract long _open_utf8(byte[] fileName, int openFlags) throws SQLException; /** * Closes the SQLite interface to a database. @@ -173,4 +172,35 @@ public abstract class DB { // TODO: add implementation throw new SQLFeatureNotSupportedException(); } + + /** + * Throws SQL Exception with error code. + * + * @param errorCode Error code to be passed. + * @throws SQLException Formatted SQLException with error code + */ + @NativeInvocation + private LimboException newSQLException(int errorCode, long errorMessagePointer) throws SQLException { + throw newSQLException(errorCode, getErrorMessage(errorMessagePointer)); + } + + /** + * Throws formatted SQLException with error code and message. + * + * @param errorCode Error code to be passed. + * @param errorMessage throw newSQLException(errorCode);Error message to be passed. + * @return Formatted SQLException with error code and message. + */ + public static LimboException newSQLException(int errorCode, String errorMessage) { + LimboErrorCode code = LimboErrorCode.getErrorCode(errorCode); + String msg; + if (code == LimboErrorCode.UNKNOWN_ERROR) { + msg = String.format("%s:%s (%s)", code, errorCode, errorMessage); + } else { + msg = String.format("%s (%s)", code, errorMessage); + } + return new LimboException(msg, code); + } + + protected abstract String getErrorMessage(long errorMessagePointer); } diff --git a/bindings/java/src/main/java/org/github/tursodatabase/core/LimboDB.java b/bindings/java/src/main/java/org/github/tursodatabase/core/LimboDB.java index 095da6910..2ed082ce6 100644 --- a/bindings/java/src/main/java/org/github/tursodatabase/core/LimboDB.java +++ b/bindings/java/src/main/java/org/github/tursodatabase/core/LimboDB.java @@ -1,65 +1,67 @@ package org.github.tursodatabase.core; +import org.github.tursodatabase.LimboErrorCode; + +import java.nio.charset.StandardCharsets; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; /** * This class provides a thin JNI layer over the SQLite3 C API. */ -public final class LimboDB extends DB { - /** - * SQLite connection handle. - */ - private long pointer = 0; +public final class LimboDB extends AbstractDB { + + // Pointer to database instance + private long dbPtr; + private boolean isOpen; private static boolean isLoaded; - private static boolean loadSucceeded; static { if ("The Android Project".equals(System.getProperty("java.vm.vendor"))) { - System.loadLibrary("sqlitejdbc"); - isLoaded = true; - loadSucceeded = true; + // TODO } else { // continue with non Android execution path isLoaded = false; - loadSucceeded = false; } } + // url example: "jdbc:sqlite:{fileName} + + /** + * + * @param url e.g. "jdbc:sqlite:fileName + * @param fileName e.g. path to file + */ + public static LimboDB create(String url, String fileName) throws SQLException { + return new LimboDB(url, fileName); + } + // TODO: receive config as argument - public LimboDB(String url, String fileName) throws SQLException { + private LimboDB(String url, String fileName) throws SQLException { super(url, fileName); } /** * Loads the SQLite interface backend. - * - * @return True if the SQLite JDBC driver is successfully loaded; false otherwise. */ - public static boolean load() throws Exception { - if (isLoaded) return loadSucceeded; + public void load() { + if (isLoaded) return; try { System.loadLibrary("_limbo_java"); - loadSucceeded = true; + } finally { isLoaded = true; } - return loadSucceeded; } // WRAPPER FUNCTIONS //////////////////////////////////////////// - @Override - protected synchronized void _open(String file, int openFlags) throws SQLException { - // TODO: add implementation - throw new SQLFeatureNotSupportedException(); - } - // TODO: add support for JNI - synchronized native void _open_utf8(byte[] fileUtf8, int openFlags) throws SQLException; + @Override + protected synchronized native long _open_utf8(byte[] file, int openFlags) throws SQLException; // TODO: add support for JNI @Override @@ -78,6 +80,15 @@ public final class LimboDB extends DB { @Override public native void interrupt(); + @Override + protected void _open(String fileName, int openFlags) throws SQLException { + if (isOpen) { + throw newSQLException(LimboErrorCode.UNKNOWN_ERROR.code, "Already opened"); + } + dbPtr = _open_utf8(stringToUtf8ByteArray(fileName), openFlags); + isOpen = true; + } + @Override protected synchronized SafeStmtPtr prepare(String sql) throws SQLException { // TODO: add implementation @@ -91,4 +102,26 @@ public final class LimboDB extends DB { // TODO: add support for JNI @Override public synchronized native int step(long stmt); + + @Override + protected String getErrorMessage(long errorMessagePointer) { + return utf8ByteBufferToString(getErrorMessageUtf8(errorMessagePointer)); + } + + private native byte[] getErrorMessageUtf8(long errorMessagePointer); + + private static String utf8ByteBufferToString(byte[] buffer) { + if (buffer == null) { + return null; + } + + return new String(buffer, StandardCharsets.UTF_8); + } + + private static byte[] stringToUtf8ByteArray(String str) { + if (str == null) { + return null; + } + return str.getBytes(StandardCharsets.UTF_8); + } } diff --git a/bindings/java/src/main/java/org/github/tursodatabase/exceptions/LimboException.java b/bindings/java/src/main/java/org/github/tursodatabase/exceptions/LimboException.java new file mode 100644 index 000000000..d4526a818 --- /dev/null +++ b/bindings/java/src/main/java/org/github/tursodatabase/exceptions/LimboException.java @@ -0,0 +1,18 @@ +package org.github.tursodatabase.exceptions; + +import org.github.tursodatabase.LimboErrorCode; + +import java.sql.SQLException; + +public class LimboException extends SQLException { + private final LimboErrorCode resultCode; + + public LimboException(String message, LimboErrorCode resultCode) { + super(message, null, resultCode.code & 0xff); + this.resultCode = resultCode; + } + + public LimboErrorCode getResultCode() { + return resultCode; + } +} diff --git a/bindings/java/src/main/java/org/github/tursodatabase/limbo/Connection.java b/bindings/java/src/main/java/org/github/tursodatabase/limbo/Connection.java deleted file mode 100644 index aaf0f2ca2..000000000 --- a/bindings/java/src/main/java/org/github/tursodatabase/limbo/Connection.java +++ /dev/null @@ -1,67 +0,0 @@ -package org.github.tursodatabase.limbo; - -import java.lang.Exception; - -/** - * Represents a connection to the database. - * TODO: Deprecate classes under limbo package. We leave this source code for reference. - */ -public class Connection { - - // Pointer to the connection object - private final long connectionPtr; - - public Connection(long connectionPtr) { - this.connectionPtr = connectionPtr; - } - - /** - * Creates a new cursor object using this connection. - * - * @return A new Cursor object. - * @throws Exception If the cursor cannot be created. - */ - public Cursor cursor() throws Exception { - long cursorId = cursor(connectionPtr); - return new Cursor(cursorId); - } - - private native long cursor(long connectionPtr); - - /** - * Closes the connection to the database. - * - * @throws Exception If there is an error closing the connection. - */ - public void close() throws Exception { - close(connectionPtr); - } - - private native void close(long connectionPtr); - - /** - * Commits the current transaction. - * - * @throws Exception If there is an error during commit. - */ - public void commit() throws Exception { - try { - commit(connectionPtr); - } catch (Exception e) { - System.out.println("caught exception: " + e); - } - } - - private native void commit(long connectionPtr) throws Exception; - - /** - * Rolls back the current transaction. - * - * @throws Exception If there is an error during rollback. - */ - public void rollback() throws Exception { - rollback(connectionPtr); - } - - private native void rollback(long connectionPtr) throws Exception; -} diff --git a/bindings/java/src/main/java/org/github/tursodatabase/limbo/Cursor.java b/bindings/java/src/main/java/org/github/tursodatabase/limbo/Cursor.java deleted file mode 100644 index eaec47f04..000000000 --- a/bindings/java/src/main/java/org/github/tursodatabase/limbo/Cursor.java +++ /dev/null @@ -1,86 +0,0 @@ -package org.github.tursodatabase.limbo; - -/** - * Represents a database cursor. - * TODO: Deprecate classes under limbo package. We leave this source code for reference. - */ -public class Cursor { - private long cursorPtr; - - public Cursor(long cursorPtr) { - this.cursorPtr = cursorPtr; - } - - // TODO: support parameters - public Cursor execute(String sql) { - var result = execute(cursorPtr, sql); - System.out.println("resut: " + result); - return this; - } - - private static native int execute(long cursorPtr, String sql); - - public Object fetchOne() throws Exception { - Object result = fetchOne(cursorPtr); - return processSingleResult(result); - } - - private static native Object fetchOne(long cursorPtr); - - public Object fetchAll() throws Exception { - Object result = fetchAll(cursorPtr); - return processArrayResult(result); - } - - private static native Object fetchAll(long cursorPtr); - - private Object processSingleResult(Object result) throws Exception { - if (result instanceof Object[]) { - System.out.println("The result is of type: Object[]"); - for (Object element : (Object[]) result) { - printElementType(element); - } - return result; - } else { - printElementType(result); - return result; - } - } - - private Object processArrayResult(Object result) throws Exception { - if (result instanceof Object[][]) { - System.out.println("The result is of type: Object[][]"); - Object[][] array = (Object[][]) result; - for (Object[] row : array) { - for (Object element : row) { - printElementType(element); - } - } - return array; - } else { - throw new Exception("result should be of type Object[][]. Maybe internal logic has error."); - } - } - - private void printElementType(Object element) { - if (element instanceof String) { - System.out.println("String: " + element); - } else if (element instanceof Integer) { - System.out.println("Integer: " + element); - } else if (element instanceof Double) { - System.out.println("Double: " + element); - } else if (element instanceof Boolean) { - System.out.println("Boolean: " + element); - } else if (element instanceof Long) { - System.out.println("Long: " + element); - } else if (element instanceof byte[]) { - System.out.print("byte[]: "); - for (byte b : (byte[]) element) { - System.out.print(b + " "); - } - System.out.println(); - } else { - System.out.println("Unknown type: " + element); - } - } -} diff --git a/bindings/java/src/main/java/org/github/tursodatabase/limbo/Limbo.java b/bindings/java/src/main/java/org/github/tursodatabase/limbo/Limbo.java deleted file mode 100644 index 941ec4656..000000000 --- a/bindings/java/src/main/java/org/github/tursodatabase/limbo/Limbo.java +++ /dev/null @@ -1,34 +0,0 @@ -package org.github.tursodatabase.limbo; - -import org.github.tursodatabase.exceptions.ErrorCode; - -import java.lang.Exception; - -/** - * TODO: Deprecate classes under limbo package. We leave this source code for reference. - */ -public class Limbo { - - private static volatile boolean initialized; - - private Limbo() { - if (!initialized) { - System.loadLibrary("_limbo_java"); - initialized = true; - } - } - - public static Limbo create() { - return new Limbo(); - } - - public Connection getConnection(String path) throws Exception { - long connectionId = connect(path); - if (connectionId == ErrorCode.CONNECTION_FAILURE) { - throw new Exception("Failed to initialize connection"); - } - return new Connection(connectionId); - } - - private static native long connect(String path); -} diff --git a/bindings/java/src/test/java/org/github/tursodatabase/TestUtils.java b/bindings/java/src/test/java/org/github/tursodatabase/TestUtils.java new file mode 100644 index 000000000..0d7e64488 --- /dev/null +++ b/bindings/java/src/test/java/org/github/tursodatabase/TestUtils.java @@ -0,0 +1,13 @@ +package org.github.tursodatabase; + +import java.io.IOException; +import java.nio.file.Files; + +public class TestUtils { + /** + * Create temporary file and returns the path. + */ + public static String createTempFile() throws IOException { + return Files.createTempFile("limbo_test_db", null).toAbsolutePath().toString(); + } +} diff --git a/bindings/java/src/test/java/org/github/tursodatabase/core/LimboDBTest.java b/bindings/java/src/test/java/org/github/tursodatabase/core/LimboDBTest.java new file mode 100644 index 000000000..8a62ea083 --- /dev/null +++ b/bindings/java/src/test/java/org/github/tursodatabase/core/LimboDBTest.java @@ -0,0 +1,29 @@ +package org.github.tursodatabase.core; + +import org.github.tursodatabase.TestUtils; +import org.junit.jupiter.api.Test; + +import java.sql.SQLException; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class LimboDBTest { + + @Test + void db_should_open_normally() throws Exception { + String dbPath = TestUtils.createTempFile(); + LimboDB db = LimboDB.create("jdbc:sqlite" + dbPath, dbPath); + db.load(); + db.open(0); + } + + @Test + void should_throw_exception_when_opened_twice() throws Exception { + String dbPath = TestUtils.createTempFile(); + LimboDB db = LimboDB.create("jdbc:sqlite:" + dbPath, dbPath); + db.load(); + db.open(0); + + assertThatThrownBy(() -> db.open(0)).isInstanceOf(SQLException.class); + } +}