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: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Keep the vendored upstream tree byte-identical; export-upstream checksums it.
upstream-xerial_sqlite-jdbc/** -text whitespace=cr-at-eol,-blank-at-eol,-blank-at-eof
38 changes: 33 additions & 5 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,14 @@ mise exec -- ./mvnw -version

```text
libsql-java/
├── libsql-hrana-codec/ # Core protocol data structures & DirectHranaJsonCodec
├── libsql-client/ # Low-level HTTP transport, baton state management, pipeline queue
├── libsql-jdbc/ # Standard JDBC 4.3 driver (java.sql.* implementation)
├── libsql-itests/ # Testcontainers (sqld) and Toxiproxy integration tests
└── vendor/ # Upstream specifications and documentation (Hrana v3, sqld)
├── libsql-hrana-codec/ # Core protocol data structures & DirectHranaJsonCodec
├── libsql-client/ # Low-level HTTP transport, baton state management, pipeline queue
├── libsql-jdbc/ # Standard JDBC 4.3 driver (java.sql.* implementation)
├── libsql-itests/ # Testcontainers (sqld) and Toxiproxy integration tests
├── libsql-conformance/ # Selected xerial/sqlite-jdbc tests running against libSQL
├── tools/ # Maintainer scripts such as export-upstream
├── upstream-xerial_sqlite-jdbc/ # Vendored xerial/sqlite-jdbc sources for conformance tests
└── vendor/ # Upstream specifications and documentation (Hrana v3, sqld)
```

### Module Guidelines
Expand Down Expand Up @@ -85,6 +88,31 @@ To run a specific test:
mise exec -- ./mvnw -pl libsql-itests -Dtest=HikariCpIntegrationIT test
```

### nono Sandbox and Docker Config

This environment may run inside a `nono` filesystem sandbox. The sandbox can block reads of `~/.docker/config.json` even when `docker info` works. Testcontainers then fails with:

```text
ServiceConfigurationError: ... EnvironmentAndSystemPropertyClientProviderStrategy could not be instantiated
Caused by: java.io.FileNotFoundException: /home/<user>/.docker/config.json (Permission denied)
```

Detect the sandbox with `nono status`, or check the exact path with:

```bash
nono why --self --path ~/.docker/config.json --op read
```

Do not change file permissions or the nono profile from inside the sandbox. `libsql-conformance` uses a readable Docker config automatically. It copies `src/test/docker-config/config.json` to `target/docker-config` and sets `DOCKER_CONFIG` for the Failsafe fork. Use that empty auth config only for public images.

To use a readable copy of your real Docker config, override the module property:

```bash
./mvnw -pl libsql-conformance -am -Ddocker.config.dir=/path/to/docker-config verify
```

Setting `DOCKER_CONFIG` in the shell is not enough by itself because the Failsafe fork receives its own value from `docker.config.dir`.

---

## 5. Coding Standards & Conventions
Expand Down
135 changes: 135 additions & 0 deletions libsql-conformance/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.github.magnusp.libsql</groupId>
<artifactId>libsql-java-parent</artifactId>
<version>${revision}</version>
</parent>

<artifactId>libsql-conformance</artifactId>
<name>libSQL xerial Conformance Tests</name>
<description>Selected xerial/sqlite-jdbc tests running against libSQL</description>

<properties>
<docker.config.dir>${project.build.directory}/docker-config</docker.config.dir>
</properties>

<dependencies>
<dependency>
<groupId>com.github.magnusp.libsql</groupId>
<artifactId>libsql-jdbc</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-launcher</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>2.0.19</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.3.1</version>
<executions>
<execution>
<id>copy-xerial-test-sources</id>
<phase>generate-test-sources</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/generated-test-sources/xerial</outputDirectory>
<resources>
<resource>
<directory>${project.basedir}/../upstream-xerial_sqlite-jdbc/src/test/java</directory>
<filtering>false</filtering>
</resource>
</resources>
</configuration>
</execution>
<execution>
<id>copy-docker-config</id>
<phase>pre-integration-test</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/docker-config</outputDirectory>
<resources>
<resource>
<directory>${project.basedir}/src/test/docker-config</directory>
<filtering>false</filtering>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>3.6.1</version>
<executions>
<execution>
<id>add-xerial-test-sources</id>
<phase>generate-test-sources</phase>
<goals>
<goal>add-test-source</goal>
</goals>
<configuration>
<sources>
<source>${project.build.directory}/generated-test-sources/xerial</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<configuration>
<includes>
<include>**/*Test.java</include>
</includes>
<environmentVariables>
<DOCKER_CONFIG>${docker.config.dir}</DOCKER_CONFIG>
</environmentVariables>
</configuration>
</plugin>
</plugins>
</build>
</project>
1 change: 1 addition & 0 deletions libsql-conformance/src/test/docker-config/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"auths":{}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
package com.github.magnusp.libsql.conformance;

import com.github.magnusp.libsql.jdbc.LibsqlDriver;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.DriverPropertyInfo;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.Properties;
import java.util.UUID;
import java.util.logging.Logger;

public final class XerialConformanceDriver implements Driver {

private static final String PREFIX = "jdbc:sqlite:";
private static final String HTTP_PREFIX = PREFIX + "http://";
private static final String HTTPS_PREFIX = PREFIX + "https://";

private final LibsqlDriver delegate = new LibsqlDriver();

public static void register() {
try {
DriverManager.registerDriver(new XerialConformanceDriver());
} catch (SQLException e) {
throw new ExceptionInInitializerError(e);
}
}

@Override
public Connection connect(String url, Properties info) throws SQLException {
if (!acceptsURL(url)) {
return null;
}
Target target = rewrite(url);
Connection connection;
try {
connection = delegate.connect(target.url(), info);
} catch (SQLException e) {
XerialTestServer.releaseNamespace(target.namespace());
throw e;
}
if (connection == null) {
XerialTestServer.releaseNamespace(target.namespace());
return null;
}
try {
return (Connection)
Proxy.newProxyInstance(
XerialConformanceDriver.class.getClassLoader(),
new Class<?>[] {Connection.class},
new NamespaceClosingHandler(connection, target.namespace()));
} catch (RuntimeException e) {
XerialTestServer.releaseNamespace(target.namespace());
throw e;
}
}

@Override
public boolean acceptsURL(String url) {
if (url == null || !url.startsWith(PREFIX)) {
return false;
}
return !url.startsWith(HTTP_PREFIX) && !url.startsWith(HTTPS_PREFIX);
}

@Override
public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException {
return delegate.getPropertyInfo(url, info);
}

@Override
public int getMajorVersion() {
return delegate.getMajorVersion();
}

@Override
public int getMinorVersion() {
return delegate.getMinorVersion();
}

@Override
public boolean jdbcCompliant() {
return delegate.jdbcCompliant();
}

@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
return delegate.getParentLogger();
}

private static Target rewrite(String url) {
String target = url.substring(PREFIX.length());
String namespace;
if (target.isEmpty() || ":memory:".equals(target)) {
namespace = "mem_" + UUID.randomUUID().toString().replace("-", "");
} else {
namespace =
"file_"
+ UUID.nameUUIDFromBytes(target.getBytes(StandardCharsets.UTF_8))
.toString()
.replace("-", "");
}
return new Target(XerialTestServer.jdbcUrl(namespace), namespace);
}

private record Target(String url, String namespace) {}

private static final class NamespaceClosingHandler implements InvocationHandler {

private final Connection connection;
private final String namespace;
private boolean closed;

private NamespaceClosingHandler(Connection connection, String namespace) {
this.connection = connection;
this.namespace = namespace;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("close".equals(method.getName()) && method.getParameterCount() == 0) {
close();
return null;
}
try {
return method.invoke(connection, args);
} catch (InvocationTargetException e) {
throw e.getCause();
}
}

private synchronized void close() throws SQLException {
if (closed) {
return;
}
closed = true;
try {
connection.close();
} finally {
XerialTestServer.releaseNamespace(namespace);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.github.magnusp.libsql.conformance;

import org.junit.platform.launcher.LauncherSession;
import org.junit.platform.launcher.LauncherSessionListener;

public final class XerialLauncherSessionListener implements LauncherSessionListener {

@Override
public void launcherSessionOpened(LauncherSession session) {
XerialConformanceDriver.register();
}

@Override
public void launcherSessionClosed(LauncherSession session) {
XerialTestServer.stop();
}
}
Loading
Loading