Styles
ci / checks (push) Successful in 39m31s

This commit is contained in:
khodowany
2026-09-08 22:07:31 -05:00
parent 6e7db6fd97
commit fa7485d404
28 changed files with 522 additions and 292 deletions
+100 -12
View File
@@ -209,13 +209,12 @@ void expect_down_stem_connected(vamp_zero::score_layout_item const& head, vamp_z
auto test_database_config(std::filesystem::path const& path) -> vamp_zero::database_config {
return {
.path = path.string(),
.migrations_path = (std::filesystem::path{VAMP_TEST_SOURCE_DIR} / "package" / "migrations").string(),
};
}
auto run_migrate(vamp_zero::database_config config) -> std::expected<void, modulus::error> {
auto run_ensure_database(vamp_zero::database_config config) -> std::expected<void, modulus::error> {
auto io = boost::asio::io_context{};
auto future = boost::asio::co_spawn(io, vamp_zero::async_migrate_database(io.get_executor(), std::move(config)), boost::asio::use_future);
auto future = boost::asio::co_spawn(io, vamp_zero::async_ensure_database(io.get_executor(), std::move(config)), boost::asio::use_future);
io.run();
return future.get();
}
@@ -264,9 +263,14 @@ auto integer_value(noto::sqlite_value const& value) -> std::int64_t {
return integer == nullptr ? 0 : *integer;
}
auto has_value(noto::query_result const& result, std::size_t column, std::string_view expected) -> std::bool_t {
return std::any_of(result.rows.begin(), result.rows.end(), [&](auto const& row) {
return row.size() > column && text_value(row[column]) == expected;
});
}
auto score_asset_json(std::string title, std::string key = "C") -> std::string {
return std::string{R"json({
"version": 1,
"metadata": {
"title": ")json"} + title + R"json(",
"composer": "Composer",
@@ -318,7 +322,6 @@ void write_text(std::filesystem::path const& path, std::string const& text) {
BOOST_AUTO_TEST_CASE(score_parser_preserves_notation_preamble) {
auto parsed = vamp_zero::parse_score_document(R"json(
{
"version": 1,
"metadata": {
"title": "Standard Tune",
"composer": "Composer",
@@ -467,7 +470,7 @@ BOOST_AUTO_TEST_CASE(tied_rests_hidden_and_slash_notes_do_not_emit_accidentals)
BOOST_AUTO_TEST_CASE(pico_remote_display_parser_accepts_crlf_payloads) {
auto state = vamp_pico::remote_display_state{};
BOOST_REQUIRE(vamp_pico::parse_remote_display_body("version=1\r\nactive=1\r\ntitle=Autumn Leaves\r\npart=Vocal\r\nmode=score\r\npage=2\r\npages=11\r\n", state));
BOOST_REQUIRE(vamp_pico::parse_remote_display_body("active=1\r\ntitle=Autumn Leaves\r\npart=Vocal\r\nmode=score\r\npage=2\r\npages=11\r\n", state));
BOOST_TEST(state.active);
BOOST_TEST(std::strcmp(state.title, "Autumn Leaves") == 0);
@@ -477,19 +480,17 @@ BOOST_AUTO_TEST_CASE(pico_remote_display_parser_accepts_crlf_payloads) {
BOOST_TEST(state.pages == 11U);
}
BOOST_AUTO_TEST_CASE(pico_remote_display_parser_rejects_missing_version_and_bad_numbers) {
BOOST_AUTO_TEST_CASE(pico_remote_display_parser_rejects_bad_numbers_and_preserves_state) {
auto state = vamp_pico::remote_display_state{};
std::snprintf(state.title, sizeof(state.title), "Keep");
BOOST_TEST(!vamp_pico::parse_remote_display_body("title=Autumn Leaves\npage=1\npages=2\n", state));
BOOST_TEST(std::strcmp(state.title, "Keep") == 0);
BOOST_TEST(!vamp_pico::parse_remote_display_body("version=1\npage=1x\npages=2\n", state));
BOOST_TEST(!vamp_pico::parse_remote_display_body("page=1x\npages=2\n", state));
BOOST_TEST(std::strcmp(state.title, "Keep") == 0);
}
BOOST_AUTO_TEST_CASE(pico_remote_display_parser_truncates_long_fields_safely) {
auto state = vamp_pico::remote_display_state{};
auto body = std::string{"version=1\ntitle="} + std::string(140U, 'A') + "\npart=" + std::string(80U, 'B') + "\nmode=performance-score";
auto body = std::string{"title="} + std::string(140U, 'A') + "\npart=" + std::string(80U, 'B') + "\nmode=performance-score";
BOOST_REQUIRE(vamp_pico::parse_remote_display_body(body, state));
BOOST_TEST(std::strlen(state.title) == sizeof(state.title) - 1U);
@@ -497,6 +498,93 @@ BOOST_AUTO_TEST_CASE(pico_remote_display_parser_truncates_long_fields_safely) {
BOOST_TEST(std::strcmp(state.mode, "performance-score") == 0);
}
BOOST_AUTO_TEST_CASE(database_ensure_creates_current_schema_without_version_bookkeeping) {
auto root = std::filesystem::temp_directory_path()
/ ("product-vamp-db-schema-test-" + std::to_string(std::chrono::steady_clock::now().time_since_epoch().count()));
auto database_path = root / "vamp.db";
std::filesystem::remove_all(root);
auto config = test_database_config(database_path);
BOOST_REQUIRE(run_ensure_database(config));
BOOST_REQUIRE(run_ensure_database(config));
auto tables = query_database(database_path, "SELECT name FROM sqlite_schema WHERE type = 'table' ORDER BY name");
BOOST_REQUIRE(tables);
BOOST_TEST(has_value(*tables, 0U, "songs"));
BOOST_TEST(has_value(*tables, 0U, "practice_exercises"));
BOOST_TEST(has_value(*tables, 0U, "page_state"));
BOOST_TEST(!has_value(*tables, 0U, "_noto_schema_migrations"));
auto version = query_database(database_path, "PRAGMA user_version");
BOOST_REQUIRE(version);
BOOST_REQUIRE_EQUAL(version->rows.size(), 1U);
BOOST_TEST(integer_value(version->rows[0][0]) == 0);
std::filesystem::remove_all(root);
}
BOOST_AUTO_TEST_CASE(database_ensure_updates_legacy_schema_in_place) {
auto root = std::filesystem::temp_directory_path()
/ ("product-vamp-db-legacy-test-" + std::to_string(std::chrono::steady_clock::now().time_since_epoch().count()));
auto database_path = root / "vamp.db";
std::filesystem::remove_all(root);
std::filesystem::create_directories(root);
BOOST_REQUIRE(query_database(database_path, R"sql(
CREATE TABLE songs (
id TEXT PRIMARY KEY NOT NULL CHECK(length(trim(id)) BETWEEN 1 AND 120),
title TEXT NOT NULL CHECK(length(trim(title)) BETWEEN 1 AND 200),
composer TEXT NOT NULL DEFAULT '',
source_json TEXT NOT NULL CHECK(json_valid(source_json)),
created_at_ms INTEGER NOT NULL CHECK(created_at_ms >= 0),
updated_at_ms INTEGER NOT NULL CHECK(updated_at_ms >= created_at_ms)
) STRICT
)sql"));
BOOST_REQUIRE(query_database(database_path, R"sql(
CREATE TABLE practice_exercises (
id TEXT PRIMARY KEY NOT NULL CHECK(length(trim(id)) BETWEEN 1 AND 160),
title TEXT NOT NULL CHECK(length(trim(title)) BETWEEN 1 AND 200),
composer TEXT NOT NULL DEFAULT '',
key_signature TEXT NOT NULL DEFAULT 'C',
source_json TEXT NOT NULL CHECK(json_valid(source_json)),
source_path TEXT NOT NULL DEFAULT '',
created_at_ms INTEGER NOT NULL CHECK(created_at_ms >= 0),
updated_at_ms INTEGER NOT NULL CHECK(updated_at_ms >= created_at_ms)
) STRICT
)sql"));
BOOST_REQUIRE(query_database(database_path, "CREATE TABLE _noto_schema_migrations(id TEXT PRIMARY KEY NOT NULL) STRICT"));
BOOST_REQUIRE(query_database(database_path, "PRAGMA user_version = 5"));
auto config = test_database_config(database_path);
BOOST_REQUIRE(run_ensure_database(config));
auto song_columns = query_database(database_path, "PRAGMA table_info(songs)");
BOOST_REQUIRE(song_columns);
BOOST_TEST(has_value(*song_columns, 1U, "part"));
BOOST_TEST(has_value(*song_columns, 1U, "source_path"));
BOOST_TEST(has_value(*song_columns, 1U, "key_signature"));
BOOST_TEST(has_value(*song_columns, 1U, "catalog_managed"));
auto practice_columns = query_database(database_path, "PRAGMA table_info(practice_exercises)");
BOOST_REQUIRE(practice_columns);
BOOST_TEST(has_value(*practice_columns, 1U, "practice_type_id"));
BOOST_TEST(has_value(*practice_columns, 1U, "practice_type_title"));
BOOST_TEST(has_value(*practice_columns, 1U, "lesson_group_id"));
BOOST_TEST(has_value(*practice_columns, 1U, "lesson_group_title"));
BOOST_TEST(has_value(*practice_columns, 1U, "catalog_managed"));
auto tables = query_database(database_path, "SELECT name FROM sqlite_schema WHERE type = 'table'");
BOOST_REQUIRE(tables);
BOOST_TEST(!has_value(*tables, 0U, "_noto_schema_migrations"));
auto version = query_database(database_path, "PRAGMA user_version");
BOOST_REQUIRE(version);
BOOST_REQUIRE_EQUAL(version->rows.size(), 1U);
BOOST_TEST(integer_value(version->rows[0][0]) == 0);
std::filesystem::remove_all(root);
}
BOOST_AUTO_TEST_CASE(database_import_upserts_catalog_rows_and_keeps_unmanaged_rows) {
auto root = std::filesystem::temp_directory_path()
/ ("product-vamp-db-test-" + std::to_string(std::chrono::steady_clock::now().time_since_epoch().count()));
@@ -507,7 +595,7 @@ BOOST_AUTO_TEST_CASE(database_import_upserts_catalog_rows_and_keeps_unmanaged_ro
write_text(song_path, score_asset_json("Catalog Tune"));
auto config = test_database_config(database_path);
BOOST_REQUIRE(run_migrate(config));
BOOST_REQUIRE(run_ensure_database(config));
BOOST_REQUIRE(run_import(config, assets));
auto first = query_database(database_path, "SELECT title, created_at_ms, updated_at_ms, catalog_managed FROM songs WHERE id = 'tune'");