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
220 changes: 220 additions & 0 deletions tests/test_c_api_database_lifecycle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -419,3 +419,223 @@ TEST_F(TempFileFixture, ReadScalarRelationValid) {
psr_free_string_array(values, count);
psr_database_close(db);
}

// ============================================================================
// Additional error handling tests
// ============================================================================

TEST_F(TempFileFixture, CreateElementInNonExistentCollection) {
auto options = psr_database_options_default();
options.console_level = PSR_LOG_OFF;
auto db = psr_database_from_schema(":memory:", VALID_SCHEMA("basic.sql").c_str(), &options);
ASSERT_NE(db, nullptr);

// Try to create element in non-existent collection - should fail
auto element = psr_element_create();
psr_element_set_string(element, "label", "Test");
auto id = psr_database_create_element(db, "NonexistentCollection", element);
psr_element_destroy(element);

EXPECT_EQ(id, -1);

psr_database_close(db);
}

TEST_F(TempFileFixture, OpenReadOnlyNonExistentPath) {
auto options = psr_database_options_default();
options.console_level = PSR_LOG_OFF;
options.read_only = 1;

// Try to open non-existent file as read-only
auto db = psr_database_open("nonexistent_path_12345.db", &options);

// Should fail because file doesn't exist and we can't create in read-only mode
EXPECT_EQ(db, nullptr);
}

TEST_F(TempFileFixture, FromSchemaValidPath) {
auto options = psr_database_options_default();
options.console_level = PSR_LOG_OFF;
auto db = psr_database_from_schema(":memory:", VALID_SCHEMA("basic.sql").c_str(), &options);

ASSERT_NE(db, nullptr);
EXPECT_EQ(psr_database_is_healthy(db), 1);

psr_database_close(db);
}

// ============================================================================
// Element ID operations
// ============================================================================

TEST_F(TempFileFixture, ReadElementIdsNullDb) {
int64_t* ids = nullptr;
size_t count = 0;
auto err = psr_database_read_element_ids(nullptr, "Collection", &ids, &count);
EXPECT_EQ(err, PSR_ERROR_INVALID_ARGUMENT);
}

TEST_F(TempFileFixture, ReadElementIdsNullCollection) {
auto options = psr_database_options_default();
options.console_level = PSR_LOG_OFF;
auto db = psr_database_from_schema(":memory:", VALID_SCHEMA("collections.sql").c_str(), &options);
ASSERT_NE(db, nullptr);

int64_t* ids = nullptr;
size_t count = 0;
auto err = psr_database_read_element_ids(db, nullptr, &ids, &count);
EXPECT_EQ(err, PSR_ERROR_INVALID_ARGUMENT);

psr_database_close(db);
}

TEST_F(TempFileFixture, ReadElementIdsNullOutput) {
auto options = psr_database_options_default();
options.console_level = PSR_LOG_OFF;
auto db = psr_database_from_schema(":memory:", VALID_SCHEMA("collections.sql").c_str(), &options);
ASSERT_NE(db, nullptr);

size_t count = 0;
auto err = psr_database_read_element_ids(db, "Collection", nullptr, &count);
EXPECT_EQ(err, PSR_ERROR_INVALID_ARGUMENT);

int64_t* ids = nullptr;
err = psr_database_read_element_ids(db, "Collection", &ids, nullptr);
EXPECT_EQ(err, PSR_ERROR_INVALID_ARGUMENT);

psr_database_close(db);
}

TEST_F(TempFileFixture, ReadElementIdsValid) {
auto options = psr_database_options_default();
options.console_level = PSR_LOG_OFF;
auto db = psr_database_from_schema(":memory:", VALID_SCHEMA("collections.sql").c_str(), &options);
ASSERT_NE(db, nullptr);

// Create Configuration first
auto config = psr_element_create();
psr_element_set_string(config, "label", "Config");
psr_database_create_element(db, "Configuration", config);
psr_element_destroy(config);

// Create some elements
for (int i = 1; i <= 3; ++i) {
auto element = psr_element_create();
psr_element_set_string(element, "label", ("Item " + std::to_string(i)).c_str());
psr_database_create_element(db, "Collection", element);
psr_element_destroy(element);
}

// Read element IDs
int64_t* ids = nullptr;
size_t count = 0;
auto err = psr_database_read_element_ids(db, "Collection", &ids, &count);
EXPECT_EQ(err, PSR_OK);
EXPECT_EQ(count, 3);

if (ids != nullptr) {
free(ids);
}

psr_database_close(db);
}

// ============================================================================
// Delete element tests
// ============================================================================

TEST_F(TempFileFixture, DeleteElementNullDb) {
auto err = psr_database_delete_element_by_id(nullptr, "Collection", 1);
EXPECT_EQ(err, PSR_ERROR_INVALID_ARGUMENT);
}

TEST_F(TempFileFixture, DeleteElementNullCollection) {
auto options = psr_database_options_default();
options.console_level = PSR_LOG_OFF;
auto db = psr_database_from_schema(":memory:", VALID_SCHEMA("collections.sql").c_str(), &options);
ASSERT_NE(db, nullptr);

auto err = psr_database_delete_element_by_id(db, nullptr, 1);
EXPECT_EQ(err, PSR_ERROR_INVALID_ARGUMENT);

psr_database_close(db);
}

TEST_F(TempFileFixture, DeleteElementValid) {
auto options = psr_database_options_default();
options.console_level = PSR_LOG_OFF;
auto db = psr_database_from_schema(":memory:", VALID_SCHEMA("collections.sql").c_str(), &options);
ASSERT_NE(db, nullptr);

// Create Configuration first
auto config = psr_element_create();
psr_element_set_string(config, "label", "Config");
psr_database_create_element(db, "Configuration", config);
psr_element_destroy(config);

// Create element
auto element = psr_element_create();
psr_element_set_string(element, "label", "Item 1");
int64_t id = psr_database_create_element(db, "Collection", element);
psr_element_destroy(element);

EXPECT_GT(id, 0);

// Delete element
auto err = psr_database_delete_element_by_id(db, "Collection", id);
EXPECT_EQ(err, PSR_OK);

// Verify element is deleted
int64_t* ids = nullptr;
size_t count = 0;
psr_database_read_element_ids(db, "Collection", &ids, &count);
EXPECT_EQ(count, 0);

if (ids != nullptr) {
free(ids);
}

psr_database_close(db);
}

// ============================================================================
// Update element tests
// ============================================================================

TEST_F(TempFileFixture, UpdateElementNullDb) {
auto element = psr_element_create();
psr_element_set_string(element, "label", "New Label");

auto err = psr_database_update_element(nullptr, "Collection", 1, element);
EXPECT_EQ(err, PSR_ERROR_INVALID_ARGUMENT);

psr_element_destroy(element);
}

TEST_F(TempFileFixture, UpdateElementNullCollection) {
auto options = psr_database_options_default();
options.console_level = PSR_LOG_OFF;
auto db = psr_database_from_schema(":memory:", VALID_SCHEMA("collections.sql").c_str(), &options);
ASSERT_NE(db, nullptr);

auto element = psr_element_create();
psr_element_set_string(element, "label", "New Label");

auto err = psr_database_update_element(db, nullptr, 1, element);
EXPECT_EQ(err, PSR_ERROR_INVALID_ARGUMENT);

psr_element_destroy(element);
psr_database_close(db);
}

TEST_F(TempFileFixture, UpdateElementNullElement) {
auto options = psr_database_options_default();
options.console_level = PSR_LOG_OFF;
auto db = psr_database_from_schema(":memory:", VALID_SCHEMA("collections.sql").c_str(), &options);
ASSERT_NE(db, nullptr);

auto err = psr_database_update_element(db, "Collection", 1, nullptr);
EXPECT_EQ(err, PSR_ERROR_INVALID_ARGUMENT);

psr_database_close(db);
}
145 changes: 145 additions & 0 deletions tests/test_database_errors.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -323,3 +323,148 @@ TEST(DatabaseErrors, UpdateSetStringsCollectionNotFound) {

EXPECT_THROW(db.update_set_strings("NonexistentCollection", "tag", 1, {"a", "b"}), std::exception);
}

// ============================================================================
// Read scalar with non-existent attribute tests
// ============================================================================

TEST(DatabaseErrors, ReadScalarIntegersAttributeNotFound) {
auto db = psr::Database::from_schema(":memory:", VALID_SCHEMA("basic.sql"), {.console_level = psr::LogLevel::off});

psr::Element e;
e.set("label", std::string("Test"));
db.create_element("Configuration", e);

// Reading non-existent column throws because SQL is invalid
EXPECT_THROW(db.read_scalar_integers("Configuration", "nonexistent_attribute"), std::runtime_error);
}

TEST(DatabaseErrors, ReadScalarDoublesAttributeNotFound) {
auto db = psr::Database::from_schema(":memory:", VALID_SCHEMA("basic.sql"), {.console_level = psr::LogLevel::off});

psr::Element e;
e.set("label", std::string("Test"));
db.create_element("Configuration", e);

EXPECT_THROW(db.read_scalar_doubles("Configuration", "nonexistent_attribute"), std::runtime_error);
}

TEST(DatabaseErrors, ReadScalarStringsAttributeNotFound) {
auto db = psr::Database::from_schema(":memory:", VALID_SCHEMA("basic.sql"), {.console_level = psr::LogLevel::off});

psr::Element e;
e.set("label", std::string("Test"));
db.create_element("Configuration", e);

EXPECT_THROW(db.read_scalar_strings("Configuration", "nonexistent_attribute"), std::runtime_error);
}

// ============================================================================
// Read vector with non-existent attribute tests
// ============================================================================

TEST(DatabaseErrors, ReadVectorIntegersAttributeNotFound) {
auto db =
psr::Database::from_schema(":memory:", VALID_SCHEMA("collections.sql"), {.console_level = psr::LogLevel::off});

psr::Element config;
config.set("label", std::string("Config"));
db.create_element("Configuration", config);

EXPECT_THROW(db.read_vector_integers("Collection", "nonexistent_attribute"), std::exception);
}

TEST(DatabaseErrors, ReadVectorDoublesAttributeNotFound) {
auto db =
psr::Database::from_schema(":memory:", VALID_SCHEMA("collections.sql"), {.console_level = psr::LogLevel::off});

psr::Element config;
config.set("label", std::string("Config"));
db.create_element("Configuration", config);

EXPECT_THROW(db.read_vector_doubles("Collection", "nonexistent_attribute"), std::exception);
}

TEST(DatabaseErrors, ReadVectorStringsAttributeNotFound) {
auto db =
psr::Database::from_schema(":memory:", VALID_SCHEMA("collections.sql"), {.console_level = psr::LogLevel::off});

psr::Element config;
config.set("label", std::string("Config"));
db.create_element("Configuration", config);

EXPECT_THROW(db.read_vector_strings("Collection", "nonexistent_attribute"), std::exception);
}

// ============================================================================
// Read set with non-existent attribute tests
// ============================================================================

TEST(DatabaseErrors, ReadSetIntegersAttributeNotFound) {
auto db =
psr::Database::from_schema(":memory:", VALID_SCHEMA("collections.sql"), {.console_level = psr::LogLevel::off});

psr::Element config;
config.set("label", std::string("Config"));
db.create_element("Configuration", config);

EXPECT_THROW(db.read_set_integers("Collection", "nonexistent_attribute"), std::exception);
}

TEST(DatabaseErrors, ReadSetDoublesAttributeNotFound) {
auto db =
psr::Database::from_schema(":memory:", VALID_SCHEMA("collections.sql"), {.console_level = psr::LogLevel::off});

psr::Element config;
config.set("label", std::string("Config"));
db.create_element("Configuration", config);

EXPECT_THROW(db.read_set_doubles("Collection", "nonexistent_attribute"), std::exception);
}

// ============================================================================
// Schema file error tests
// ============================================================================

TEST(DatabaseErrors, ApplySchemaEmptyPath) {
EXPECT_THROW(psr::Database::from_schema(":memory:", "", {.console_level = psr::LogLevel::off}), std::runtime_error);
}

TEST(DatabaseErrors, ApplySchemaFileNotFound) {
EXPECT_THROW(
psr::Database::from_schema(":memory:", "nonexistent/path/schema.sql", {.console_level = psr::LogLevel::off}),
std::runtime_error);
}

// ============================================================================
// Update scalar with collection not found
// ============================================================================

TEST(DatabaseErrors, UpdateScalarIntegerCollectionNotFound) {
auto db = psr::Database::from_schema(":memory:", VALID_SCHEMA("basic.sql"), {.console_level = psr::LogLevel::off});

EXPECT_THROW(db.update_scalar_integer("NonexistentCollection", "value", 1, 42), std::runtime_error);
}

TEST(DatabaseErrors, UpdateScalarDoubleCollectionNotFound) {
auto db = psr::Database::from_schema(":memory:", VALID_SCHEMA("basic.sql"), {.console_level = psr::LogLevel::off});

EXPECT_THROW(db.update_scalar_double("NonexistentCollection", "value", 1, 3.14), std::runtime_error);
}

TEST(DatabaseErrors, UpdateScalarStringCollectionNotFound) {
auto db = psr::Database::from_schema(":memory:", VALID_SCHEMA("basic.sql"), {.console_level = psr::LogLevel::off});

EXPECT_THROW(db.update_scalar_string("NonexistentCollection", "value", 1, "test"), std::runtime_error);
}

// ============================================================================
// Read element IDs errors
// ============================================================================

TEST(DatabaseErrors, ReadElementIdsNoSchema) {
psr::Database db(":memory:", {.console_level = psr::LogLevel::off});

// Without schema, executing SQL will fail due to missing table
EXPECT_THROW(db.read_element_ids("Configuration"), std::runtime_error);
}
Loading
Loading