Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
6c19025
WIP
suddendust Feb 25, 2026
141636f
WIP
suddendust Feb 26, 2026
4e3d628
Fixed test cases
suddendust Feb 26, 2026
16abaac
Added test cases
suddendust Feb 26, 2026
a280617
Merge branch 'main' of github.com:hypertrace/document-store into pg_w…
suddendust Feb 26, 2026
d963dc7
Fix failing test
suddendust Feb 26, 2026
4f85ff4
Refactor
suddendust Feb 26, 2026
99a0b32
Rollback inadvertent changes
suddendust Feb 26, 2026
651a3ea
WIP
suddendust Feb 26, 2026
63c8691
Add MongoFlatPgConsistencyTest
suddendust Feb 26, 2026
75b5943
Added consistency tests for deeply nested fields
suddendust Feb 26, 2026
e7aabb6
Spotless
suddendust Feb 26, 2026
c6953a9
WIP
suddendust Mar 6, 2026
86fec8b
WIP
suddendust Mar 6, 2026
d7540e2
WIP
suddendust Mar 6, 2026
0aeb9b2
WIP
suddendust Mar 6, 2026
086fbce
WIP
suddendust Mar 6, 2026
45d430a
WIP
suddendust Mar 6, 2026
a1b2208
WIP
suddendust Mar 6, 2026
9ffdc44
Revert "WIP"
suddendust Mar 6, 2026
a81968a
Reapply "WIP"
suddendust Mar 6, 2026
3bf82ae
WIP
suddendust Mar 6, 2026
8313386
WIP
suddendust Mar 7, 2026
3d69e41
Merge branch 'main' of github.com:hypertrace/document-store into bugf…
suddendust Mar 9, 2026
ee06fc7
WIP
suddendust Mar 9, 2026
545439a
Merge remote-tracking branch 'myfork/bugfix_setSubDocOp' into bugfix_…
suddendust Mar 9, 2026
c509cab
Refactor
suddendust Mar 9, 2026
950895c
Spotless
suddendust Mar 9, 2026
bda4cbd
Add missing file
suddendust Mar 9, 2026
75ddcbd
Add missing file
suddendust Mar 9, 2026
84b20b9
WIP
suddendust Mar 9, 2026
2a0eb52
Remove assertion on date
suddendust Mar 9, 2026
5094189
Fix upsert behaviour for Mongo
suddendust Mar 9, 2026
f9e40c5
Fix failing test cases
suddendust Mar 9, 2026
d307670
Remove messages in assertions
suddendust Mar 9, 2026
b56210e
Handle jsonb col names correctly for flat collections
suddendust Mar 11, 2026
2be3bab
WIP
suddendust Mar 12, 2026
9f40ac3
WIP
suddendust Mar 12, 2026
7a3b611
Unset based on type
suddendust Mar 12, 2026
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
@@ -0,0 +1,215 @@
package org.hypertrace.core.documentstore;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.typesafe.config.ConfigFactory;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Stream;
import org.hypertrace.core.documentstore.expression.impl.ConstantExpression;
import org.hypertrace.core.documentstore.expression.impl.IdentifierExpression;
import org.hypertrace.core.documentstore.expression.impl.RelationalExpression;
import org.hypertrace.core.documentstore.expression.operators.RelationalOperator;
import org.hypertrace.core.documentstore.model.options.MissingColumnStrategy;
import org.hypertrace.core.documentstore.postgres.PostgresDatastore;
import org.hypertrace.core.documentstore.query.Query;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.ArgumentsProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.utility.DockerImageName;

/** Base class for write tests */
public abstract class BaseWriteTest {

protected static final Logger LOGGER = LoggerFactory.getLogger(BaseWriteTest.class);
protected static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
protected static final String DEFAULT_TENANT = "default";

// MongoDB container and datastore - shared by all subclasses
protected static GenericContainer<?> mongoContainer;
protected static Datastore mongoDatastore;

// PostgreSQL container and datastore - shared by all subclasses
protected static GenericContainer<?> postgresContainer;
protected static Datastore postgresDatastore;

// Maps for multi-store tests
protected static Map<String, Datastore> datastoreMap = new HashMap<>();
protected static Map<String, Collection> collectionMap = new HashMap<>();

protected Collection getCollection(String storeName) {
return collectionMap.get(storeName);
}

private static final String FLAT_COLLECTION_SCHEMA_PATH =
"schema/flat_collection_test_schema.sql";

protected static String loadFlatCollectionSchema() {
try (InputStream is =
BaseWriteTest.class.getClassLoader().getResourceAsStream(FLAT_COLLECTION_SCHEMA_PATH);
BufferedReader reader =
new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
StringBuilder sb = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
sb.append(line).append(" ");
}
return sb.toString().trim();
} catch (Exception e) {
throw new RuntimeException("Failed to load schema from " + FLAT_COLLECTION_SCHEMA_PATH, e);
}
}

protected static void initMongo() {
mongoContainer =
new GenericContainer<>(DockerImageName.parse("mongo:8.0.1"))
.withExposedPorts(27017)
.waitingFor(Wait.forListeningPort());
mongoContainer.start();

Map<String, String> mongoConfig = new HashMap<>();
mongoConfig.put("host", "localhost");
mongoConfig.put("port", mongoContainer.getMappedPort(27017).toString());

mongoDatastore = DatastoreProvider.getDatastore("Mongo", ConfigFactory.parseMap(mongoConfig));
LOGGER.info("Mongo datastore initialized");
}

protected static void shutdownMongo() {
if (mongoContainer != null) {
mongoContainer.stop();
}
}

protected static void initPostgres() {
postgresContainer =
new GenericContainer<>(DockerImageName.parse("postgres:13.1"))
.withEnv("POSTGRES_PASSWORD", "postgres")
.withEnv("POSTGRES_USER", "postgres")
.withExposedPorts(5432)
.waitingFor(Wait.forListeningPort());
postgresContainer.start();

String postgresConnectionUrl =
String.format("jdbc:postgresql://localhost:%s/", postgresContainer.getMappedPort(5432));

Map<String, String> postgresConfig = new HashMap<>();
postgresConfig.put("url", postgresConnectionUrl);
postgresConfig.put("user", "postgres");
postgresConfig.put("password", "postgres");

postgresDatastore =
DatastoreProvider.getDatastore("Postgres", ConfigFactory.parseMap(postgresConfig));
LOGGER.info("Postgres datastore initialized");
}

protected static void shutdownPostgres() {
if (postgresContainer != null) {
postgresContainer.stop();
}
}

protected static void createFlatCollectionSchema(
PostgresDatastore pgDatastore, String tableName) {
String createTableSQL = String.format(loadFlatCollectionSchema(), tableName);

try (Connection connection = pgDatastore.getPostgresClient();
PreparedStatement statement = connection.prepareStatement(createTableSQL)) {
statement.execute();
LOGGER.info("Created flat collection table: {}", tableName);
} catch (Exception e) {
LOGGER.error("Failed to create flat collection schema: {}", e.getMessage(), e);
throw new RuntimeException("Failed to create flat collection schema", e);
}
}

protected static String generateDocId(String prefix) {
return prefix + "-" + System.currentTimeMillis() + "-" + (int) (Math.random() * 10000);
}

protected static String getKeyString(String docId) {
return new SingleValueKey(DEFAULT_TENANT, docId).toString();
}

protected Query buildQueryById(String docId) {
return Query.builder()
.setFilter(
RelationalExpression.of(
IdentifierExpression.of("id"),
RelationalOperator.EQ,
ConstantExpression.of(getKeyString(docId))))
.build();
}

protected Document createTestDocument(String docId) {
Key key = new SingleValueKey(DEFAULT_TENANT, docId);
String keyStr = key.toString();

ObjectNode objectNode = OBJECT_MAPPER.createObjectNode();
objectNode.put("id", keyStr);
objectNode.put("item", "TestItem");
objectNode.put("price", 100);
objectNode.put("quantity", 50);
objectNode.put("in_stock", true);
objectNode.put("big_number", 1000000000000L);
objectNode.put("rating", 3.5);
objectNode.put("weight", 50.0);
objectNode.putArray("tags").add("tag1").add("tag2");
objectNode.putArray("numbers").add(1).add(2).add(3);
ObjectNode props = OBJECT_MAPPER.createObjectNode();
props.put("brand", "TestBrand");
props.put("size", "M");
props.put("count", 10);
props.putArray("colors").add("red").add("blue");
objectNode.set("props", props);
ObjectNode sales = OBJECT_MAPPER.createObjectNode();
sales.put("total", 200);
sales.put("count", 10);
objectNode.set("sales", sales);

return new JSONDocument(objectNode);
}

protected Key createKey(String docId) {
return new SingleValueKey(DEFAULT_TENANT, docId);
}

protected static void clearTable(String tableName) {
PostgresDatastore pgDatastore = (PostgresDatastore) postgresDatastore;
String deleteSQL = String.format("DELETE FROM \"%s\"", tableName);
try (Connection connection = pgDatastore.getPostgresClient();
PreparedStatement statement = connection.prepareStatement(deleteSQL)) {
statement.executeUpdate();
} catch (Exception e) {
LOGGER.error("Failed to clear table {}: {}", tableName, e.getMessage(), e);
}
}

protected void insertTestDocument(String docId, Collection collection) throws IOException {
Key key = createKey(docId);
Document document = createTestDocument(docId);
collection.upsert(key, document);
}

/** Provides all MissingColumnStrategy values for parameterized tests */
protected static class MissingColumnStrategyProvider implements ArgumentsProvider {
@Override
public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
return Stream.of(
Arguments.of(MissingColumnStrategy.SKIP),
Arguments.of(MissingColumnStrategy.THROW),
Arguments.of(MissingColumnStrategy.IGNORE_DOCUMENT));
}
}
}
Loading
Loading