Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
56a74b1
spark-uc module
tdas Nov 16, 2025
5784bfe
Move Unity Catalog test support to com.sparkuctest package
tdas Nov 16, 2025
1b7fe04
Add Unity Catalog PR strategy and repository setup
tdas Nov 18, 2025
892c1d0
Fix sparkUnityCatalog to use CrossSparkVersions.sparkDependentSettings
tdas Nov 19, 2025
8fc5ac2
Make sparkUnityCatalog tests run only on Spark 4.0
tdas Nov 19, 2025
e1b8157
Sync: conditional test execution for Spark 4.0
tdas Nov 19, 2025
407f726
Address PR comments: improve logging, server readiness, test coverage…
tdas Nov 19, 2025
0778546
Address PR comments: improve logging, server readiness, test coverage…
tdas Nov 19, 2025
9017d89
Use dependency overrides to resolve Jackson conflicts instead of shad…
tdas Nov 19, 2025
3ec5f93
Use dependency overrides to resolve Jackson conflicts instead of shad…
tdas Nov 19, 2025
2f6adec
Revert sbt-assembly upgrade and cleanup module-info merge strategy
tdas Nov 19, 2025
c0fa580
Revert sbt-assembly upgrade and cleanup module-info merge strategy
tdas Nov 19, 2025
0b5fcae
Address PR comments: fix copyright year, use ucPort directly, simplif…
tdas Nov 24, 2025
327cde7
Remove unused import
tdas Nov 24, 2025
4d6a4d0
Address PR comments: fix copyright year, use ucPort directly, simplif…
tdas Nov 24, 2025
8c07bc9
Convert Unity Catalog foundation tests to Java
tdas Nov 26, 2025
bf96958
Add Delta Lake required Spark configurations
tdas Nov 26, 2025
5ef2749
redid the mobule in build.sbt
tdas Dec 1, 2025
0490fb4
Merge remote-tracking branch 'origin/master' into stack/spark-uc-pr1-…
tdas Dec 1, 2025
c1f8415
revert unnecessary changes to build.sbt
tdas Dec 1, 2025
c824a51
Change package from com.sparkuctest to io.sparkuctest
tdas Dec 2, 2025
bf4eeed
Add the test abstraction so that we can design delta integration test…
openinx Dec 3, 2025
c899412
Refactor
openinx Dec 3, 2025
405f949
Remove the existing tests.
openinx Dec 4, 2025
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
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -136,3 +136,7 @@ override.tf.json
# Ignore kernel benchmark report
kernel/kernel-benchmarks/benchmark_report.json

# Unity Catalog test artifacts
spark/unitycatalog/etc/
.scala-build/

72 changes: 71 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -705,6 +705,76 @@ lazy val contribs = (project in file("contribs"))
Compile / compile := ((Compile / compile) dependsOn createTargetClassesDir).value
).configureUnidoc()


val unityCatalogVersion = "0.3.0"
val sparkUnityCatalogJacksonVersion = "2.15.4" // We are using Spark 4.0's Jackson version 2.15.x, to override Unity Catalog 0.3.0's version 2.18.x

lazy val sparkUnityCatalog = (project in file("spark/unitycatalog"))
.dependsOn(spark % "compile->compile;test->test;provided->provided")
.disablePlugins(ScalafmtPlugin)
.settings(
name := "delta-spark-unitycatalog",
commonSettings,
skipReleaseSettings,
CrossSparkVersions.sparkDependentSettings(sparkVersion),

// This is a test-only module - no production sources
Compile / sources := Seq.empty,

// Ensure Java sources are picked up
Test / unmanagedSourceDirectories += baseDirectory.value / "src" / "test" / "java",

Test / javaOptions ++= Seq("-ea"),

// Don't execute in parallel since we can't have multiple Sparks in the same JVM
Test / parallelExecution := false,

// Force ALL Jackson dependencies to match Spark's Jackson version
// This overrides Jackson from Unity Catalog's transitive dependencies (e.g., Armeria)
dependencyOverrides ++= Seq(
"com.fasterxml.jackson.core" % "jackson-core" % sparkUnityCatalogJacksonVersion,
"com.fasterxml.jackson.core" % "jackson-annotations" % sparkUnityCatalogJacksonVersion,
"com.fasterxml.jackson.core" % "jackson-databind" % sparkUnityCatalogJacksonVersion,
"com.fasterxml.jackson.module" %% "jackson-module-scala" % sparkUnityCatalogJacksonVersion,
"com.fasterxml.jackson.dataformat" % "jackson-dataformat-yaml" % sparkUnityCatalogJacksonVersion,
"com.fasterxml.jackson.datatype" % "jackson-datatype-jsr310" % sparkUnityCatalogJacksonVersion,
"com.fasterxml.jackson.datatype" % "jackson-datatype-jdk8" % sparkUnityCatalogJacksonVersion
),

libraryDependencies ++= Seq(
// Standard test dependencies
"org.scalatest" %% "scalatest" % scalaTestVersion % "test",

// Unity Catalog dependencies - exclude Jackson to use Spark's Jackson 2.15.x
"io.unitycatalog" %% "unitycatalog-spark" % unityCatalogVersion % "test" excludeAll(
ExclusionRule(organization = "com.fasterxml.jackson.core"),
ExclusionRule(organization = "com.fasterxml.jackson.module"),
ExclusionRule(organization = "com.fasterxml.jackson.datatype"),
ExclusionRule(organization = "com.fasterxml.jackson.dataformat")
),
"io.unitycatalog" % "unitycatalog-server" % unityCatalogVersion % "test" excludeAll(
ExclusionRule(organization = "com.fasterxml.jackson.core"),
ExclusionRule(organization = "com.fasterxml.jackson.module"),
ExclusionRule(organization = "com.fasterxml.jackson.datatype"),
ExclusionRule(organization = "com.fasterxml.jackson.dataformat")
),

// Spark test dependencies
"org.apache.spark" %% "spark-sql" % sparkVersion.value % "test",
"org.apache.spark" %% "spark-catalyst" % sparkVersion.value % "test",
"org.apache.spark" %% "spark-core" % sparkVersion.value % "test",

// TODO: Let's define a common junit version.
"org.junit.jupiter" % "junit-jupiter" % "5.10.3" % Test,
"org.apache.hadoop" % "hadoop-aws" % hadoopVersion % Test,
"net.aichler" % "jupiter-interface" % JupiterKeys.jupiterVersion.value % Test,
"org.assertj" % "assertj-core" % "3.26.3" % Test,
),

Test / testOptions += Tests.Argument("-oDF"),
Test / testOptions += Tests.Argument(TestFrameworks.JUnit, "-v", "-a")
)

lazy val sharing = (project in file("sharing"))
.dependsOn(spark % "compile->compile;test->test;provided->provided")
.disablePlugins(JavaFormatterPlugin, ScalafmtPlugin)
Expand Down Expand Up @@ -1515,7 +1585,7 @@ val createTargetClassesDir = taskKey[Unit]("create target classes dir")

// Don't use these groups for any other projects
lazy val sparkGroup = project
.aggregate(spark, sparkV1, sparkV1Filtered, sparkV2, contribs, storage, storageS3DynamoDB, sharing, hudi)
.aggregate(spark, sparkV1, sparkV1Filtered, sparkV2, contribs, sparkUnityCatalog, storage, storageS3DynamoDB, sharing, hudi)
.settings(
// crossScalaVersions must be set to Nil on the aggregating project
crossScalaVersions := Nil,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright (2025) The Delta Lake Project Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.sparkuctest;

import com.google.common.collect.ImmutableList;
import org.junit.jupiter.api.Test;

public class UCManagedTableDMLTest extends UnityCatalogTestBase {

@Test
public void testBasicOperation() {
String tableName = fullTableName("testBasicOperation");
sql("CREATE TABLE %s (id INT, val STRING) USING DELTA LOCATION '%s'",
tableName, path("testBasicOperation"));

sql("INSERT INTO %s VALUES (1, 'AAA'), (2, 'BBB')", tableName);

assertEquals("Should have the expected results",
ImmutableList.of(row(1, "AAA"), row(2, "BBB")),
sql("SELECT * FROM %s ORDER BY id ASC", tableName));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/*
* Copyright (2025) The Delta Lake Project Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.sparkuctest;

import static org.assertj.core.api.Assertions.assertThat;

import com.google.common.base.Preconditions;
import io.sparkuctest.extension.UnityCatalogExtension;
import io.sparkuctest.extension.UnityCatalogExtensionUtil;
import java.util.List;
import org.apache.spark.SparkConf;
import org.apache.spark.sql.Row;
import org.apache.spark.sql.SparkSession;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.extension.RegisterExtension;

public class UnityCatalogTestBase {

@RegisterExtension
public static UnityCatalogExtension UC_EXTENSION = UnityCatalogExtensionUtil.initialize();

private static SparkSession spark;

@BeforeAll
public static void beforeAll() {
SparkConf conf = new SparkConf()
.setAppName("UnityCatalog Support Tests")
.setMaster("local[2]")
.set("spark.ui.enabled", "false")
.set("spark.sql.shuffle.partitions", "5")
// Delta Lake required configurations
.set("spark.sql.extensions", "io.delta.sql.DeltaSparkSessionExtension")
.set("spark.sql.catalog.spark_catalog", "org.apache.spark.sql.delta.catalog.DeltaCatalog");

// Configure with Unity Catalog
UC_EXTENSION.catalogSparkConf().forEach(conf::set);

// Build the spark session.
spark = SparkSession.builder().config(conf).getOrCreate();
}

@AfterAll
public static void afterAll() {
if (spark != null) {
spark.stop();
spark = null;
}
}

protected String fullTableName(String tableName) {
return String.format("`%s`.`%s`.`%s`",
UC_EXTENSION.catalogName(), UC_EXTENSION.schemaName(), tableName);
}

protected String path(String basename) {
Preconditions.checkNotNull(basename, "basename cannot be null");
if (basename.startsWith("/")) {
return String.format("%s%s", UC_EXTENSION.rootTestingDir(), basename);
} else {
return String.format("%s/%s", UC_EXTENSION.rootTestingDir(), basename);
}
}

public static List<Row> sql(String statement, Object... args) {
return spark.sql(String.format(statement, args)).collectAsList();
}

public static Object[] row(Object... args) {
return args;
}

public static void assertEquals(String context, List<Object[]> expected, List<Row> actual) {
assertThat(expected)
.as("%s: number of results should match", context)
.hasSameSizeAs(actual);

for (int row = 0; row < expected.size(); row += 1) {
Object[] expectedRow = expected.get(row);
Row actualRow = actual.get(row);

assertEquals(context + ": row " + (row + 1), expectedRow, actualRow);
}
}

public static void assertEquals(String context, Object[] expected, Row actual) {
assertThat(expected.length)
.as("%s: Number of columns should match", context)
.isEqualTo(actual.size());
for (int i = 0; i < expected.length; i += 1) {
assertThat(expected[i])
.as("%s: Element does not match", context)
.isEqualTo(actual.get(i));
}
}
}
Loading
Loading