Skip to content

Commit c677bb7

Browse files
authored
test(spark): characterize VortexCatalog path identifier resolution (#9161)
`VortexCatalog` — the path-based catalog behind ``SELECT * FROM vortex.`/path` `` — was only exercised end-to-end through `VortexSqlTest`, which starts a real session. This pins the identifier contract directly, without starting Spark. Covered: - the catalog name comes from `initialize`, and defaults to `vortex` before it is called - `listTables` is always empty: the catalog holds no state, tables are addressed by path - an identifier with no `/` is not a table, and neither is a namespaced one even when the name looks like a path - a path-shaped identifier that cannot be read is deliberately translated into `NoSuchTableException`, so SQL users see "table not found" rather than an internal error - `dropTable` reports that nothing was dropped rather than deleting data - `CREATE`, `ALTER` and `RENAME TABLE` are unsupported ## Tests - `./gradlew :vortex-spark_2.13:test --tests 'dev.vortex.spark.VortexCatalogTest'` — 10/10 pass - `./gradlew :vortex-spark_2.12:test --tests 'dev.vortex.spark.VortexCatalogTest'` — 10/10 pass - `./gradlew :vortex-spark_2.13:spotlessCheck` — clean Signed-off-by: jackylee <qcsd2011@gmail.com>
1 parent 135d055 commit c677bb7

1 file changed

Lines changed: 114 additions & 0 deletions

File tree

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// SPDX-FileCopyrightText: Copyright the Vortex contributors
3+
4+
package dev.vortex.spark;
5+
6+
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
7+
import static org.junit.jupiter.api.Assertions.assertEquals;
8+
import static org.junit.jupiter.api.Assertions.assertFalse;
9+
import static org.junit.jupiter.api.Assertions.assertThrows;
10+
11+
import java.util.Map;
12+
import org.apache.spark.sql.catalyst.analysis.NoSuchTableException;
13+
import org.apache.spark.sql.connector.catalog.Identifier;
14+
import org.apache.spark.sql.connector.catalog.TableChange;
15+
import org.apache.spark.sql.connector.expressions.Transform;
16+
import org.apache.spark.sql.types.DataTypes;
17+
import org.apache.spark.sql.types.Metadata;
18+
import org.apache.spark.sql.types.StructField;
19+
import org.apache.spark.sql.types.StructType;
20+
import org.apache.spark.sql.util.CaseInsensitiveStringMap;
21+
import org.junit.jupiter.api.DisplayName;
22+
import org.junit.jupiter.api.Test;
23+
24+
/**
25+
* Unit tests for {@link VortexCatalog}, the path-based catalog behind {@code SELECT * FROM vortex.`/path`}.
26+
*
27+
* <p>Characterizes which identifiers the catalog accepts as paths and which it rejects, plus the DDL surface it
28+
* deliberately does not implement. End-to-end querying through a real session is covered by {@code VortexSqlTest};
29+
* these tests pin the identifier contract without starting Spark.
30+
*/
31+
final class VortexCatalogTest {
32+
33+
private static final StructType SCHEMA =
34+
new StructType(new StructField[] {new StructField("id", DataTypes.IntegerType, false, Metadata.empty())});
35+
36+
private static VortexCatalog catalog() {
37+
VortexCatalog catalog = new VortexCatalog();
38+
catalog.initialize("vortex", new CaseInsensitiveStringMap(Map.of()));
39+
return catalog;
40+
}
41+
42+
@Test
43+
@DisplayName("Takes its name from the session config that registered it")
44+
void nameComesFromInitialize() {
45+
VortexCatalog catalog = new VortexCatalog();
46+
catalog.initialize("my_vortex", new CaseInsensitiveStringMap(Map.of()));
47+
48+
assertEquals("my_vortex", catalog.name());
49+
}
50+
51+
@Test
52+
@DisplayName("Defaults to the name \"vortex\" before initialize is called")
53+
void nameDefaultsToVortex() {
54+
assertEquals("vortex", new VortexCatalog().name());
55+
}
56+
57+
@Test
58+
@DisplayName("Lists no tables: the catalog holds no state, tables are addressed by path")
59+
void listTablesIsAlwaysEmpty() {
60+
assertArrayEquals(new Identifier[0], catalog().listTables(new String[0]));
61+
assertArrayEquals(new Identifier[0], catalog().listTables(new String[] {"any", "namespace"}));
62+
}
63+
64+
@Test
65+
@DisplayName("An identifier without a path separator is not a table")
66+
void identifierWithoutSlashIsRejected() {
67+
assertThrows(NoSuchTableException.class, () -> catalog().loadTable(Identifier.of(new String[0], "not_a_path")));
68+
}
69+
70+
@Test
71+
@DisplayName("A namespaced identifier is not a table, even when the name looks like a path")
72+
void namespacedIdentifierIsRejected() {
73+
assertThrows(NoSuchTableException.class, () -> catalog()
74+
.loadTable(Identifier.of(new String[] {"db"}, "/data/a.vortex")));
75+
}
76+
77+
@Test
78+
@DisplayName("A path-shaped identifier that cannot be read surfaces as table not found")
79+
void unreadablePathIsReportedAsMissingTable() {
80+
// Schema inference throws for a path with no Vortex files; the catalog translates that into
81+
// NoSuchTableException so SQL users see "table not found" rather than an internal error.
82+
assertThrows(NoSuchTableException.class, () -> catalog()
83+
.loadTable(Identifier.of(new String[0], "/nonexistent/vortex/path")));
84+
}
85+
86+
@Test
87+
@DisplayName("Dropping a table never deletes data, it reports that nothing was dropped")
88+
void dropTableReturnsFalse() {
89+
assertFalse(catalog().dropTable(Identifier.of(new String[0], "/data/a.vortex")));
90+
}
91+
92+
@Test
93+
@DisplayName("CREATE TABLE is unsupported: write to the path instead")
94+
void createTableIsUnsupported() {
95+
assertThrows(UnsupportedOperationException.class, () -> catalog()
96+
.createTable(Identifier.of(new String[0], "/data/a.vortex"), SCHEMA, new Transform[0], Map.of()));
97+
}
98+
99+
@Test
100+
@DisplayName("ALTER TABLE is unsupported: there is no metadata to alter")
101+
void alterTableIsUnsupported() {
102+
assertThrows(UnsupportedOperationException.class, () -> catalog()
103+
.alterTable(Identifier.of(new String[0], "/data/a.vortex"), new TableChange[0]));
104+
}
105+
106+
@Test
107+
@DisplayName("RENAME TABLE is unsupported: there is no metadata to rename")
108+
void renameTableIsUnsupported() {
109+
assertThrows(UnsupportedOperationException.class, () -> catalog()
110+
.renameTable(
111+
Identifier.of(new String[0], "/data/a.vortex"),
112+
Identifier.of(new String[0], "/data/b.vortex")));
113+
}
114+
}

0 commit comments

Comments
 (0)