LCOV - code coverage report
Current view: top level - backend/sqltools - catalog_names_test.cc (source / functions) Coverage Total Hit
Test: _coverage_report.dat Lines: 70.1 % 174 122
Test Date: 2026-07-02 21:01:18 Functions: 42.9 % 28 12

            Line data    Source code
       1              : #include "backend/sqltools/catalog_names.h"
       2              : 
       3              : #include <map>
       4              : #include <string>
       5              : #include <vector>
       6              : 
       7              : #include "absl/status/status.h"
       8              : #include "absl/status/statusor.h"
       9              : #include "absl/strings/str_cat.h"
      10              : #include "absl/types/span.h"
      11              : #include "backend/schema/schema.h"
      12              : #include "backend/storage/storage.h"
      13              : #include "gtest/gtest.h"
      14              : 
      15              : namespace bigquery_emulator {
      16              : namespace backend {
      17              : namespace sqltools {
      18              : namespace {
      19              : 
      20              : class FakeCatalogStorage : public storage::Storage {
      21              :  public:
      22            4 :   void AddDataset(absl::string_view project_id, absl::string_view dataset_id) {
      23            4 :     datasets_[std::string(project_id)].push_back(
      24            4 :         storage::DatasetId{std::string(project_id), std::string(dataset_id)});
      25            4 :   }
      26              : 
      27            2 :   void AddTable(const storage::TableId& id, schema::TableSchema schema) {
      28            2 :     tables_[id.dataset_id].push_back(id);
      29            2 :     schemas_[TableKey(id)] = std::move(schema);
      30            2 :   }
      31              : 
      32              :   void AddRoutine(const storage::DatasetId& dataset,
      33            1 :                   storage::RoutineRecord routine) {
      34            1 :     routines_[dataset.dataset_id].push_back(std::move(routine));
      35            1 :   }
      36              : 
      37              :   absl::Status CreateDataset(const storage::DatasetId&,
      38            0 :                              absl::string_view) override {
      39            0 :     return absl::UnimplementedError("FakeCatalogStorage::CreateDataset");
      40            0 :   }
      41              :   absl::Status DropDataset(const storage::DatasetId&,
      42              :                            bool,
      43            0 :                            absl::string_view = {}) override {
      44            0 :     return absl::UnimplementedError("FakeCatalogStorage::DropDataset");
      45            0 :   }
      46              :   absl::Status CreateTable(const storage::TableId&,
      47            0 :                            const schema::TableSchema&) override {
      48            0 :     return absl::UnimplementedError("FakeCatalogStorage::CreateTable");
      49            0 :   }
      50            0 :   absl::Status DropTable(const storage::TableId&) override {
      51            0 :     return absl::UnimplementedError("FakeCatalogStorage::DropTable");
      52            0 :   }
      53              :   absl::StatusOr<std::vector<storage::DatasetId>> ListDatasets(
      54            2 :       absl::string_view project_id) const override {
      55            2 :     auto it = datasets_.find(std::string(project_id));
      56            2 :     if (it == datasets_.end()) {
      57            0 :       return std::vector<storage::DatasetId>{};
      58            0 :     }
      59            2 :     return it->second;
      60            2 :   }
      61              :   absl::StatusOr<std::vector<storage::TableId>> ListTables(
      62            3 :       const storage::DatasetId& dataset) const override {
      63            3 :     auto it = tables_.find(dataset.dataset_id);
      64            3 :     if (it == tables_.end()) {
      65            1 :       return std::vector<storage::TableId>{};
      66            1 :     }
      67            2 :     return it->second;
      68            3 :   }
      69              :   absl::StatusOr<schema::TableSchema> GetSchema(
      70            2 :       const storage::TableId& id) const override {
      71            2 :     auto it = schemas_.find(TableKey(id));
      72            2 :     if (it == schemas_.end()) {
      73            0 :       return absl::NotFoundError("schema not found");
      74            0 :     }
      75            2 :     return it->second;
      76            2 :   }
      77              :   absl::Status AppendRows(const storage::TableId&,
      78            0 :                           absl::Span<const storage::Row>) override {
      79            0 :     return absl::UnimplementedError("FakeCatalogStorage::AppendRows");
      80            0 :   }
      81              :   absl::Status OverwriteRows(const storage::TableId&,
      82            0 :                              absl::Span<const storage::Row>) override {
      83            0 :     return absl::UnimplementedError("FakeCatalogStorage::OverwriteRows");
      84            0 :   }
      85              :   absl::StatusOr<std::unique_ptr<storage::RowIterator>> ScanRows(
      86            0 :       const storage::TableId&) const override {
      87            0 :     return absl::UnimplementedError("FakeCatalogStorage::ScanRows");
      88            0 :   }
      89              :   absl::StatusOr<std::unique_ptr<storage::RowIterator>> CreateReadStream(
      90            0 :       const storage::TableId&, const storage::ReadFilter&) const override {
      91            0 :     return absl::UnimplementedError("FakeCatalogStorage::CreateReadStream");
      92            0 :   }
      93              :   absl::StatusOr<std::int64_t> CountRows(
      94            0 :       const storage::TableId&) const override {
      95            0 :     return absl::UnimplementedError("FakeCatalogStorage::CountRows");
      96            0 :   }
      97            0 :   absl::Status UpsertRoutine(const storage::RoutineRecord&) override {
      98            0 :     return absl::UnimplementedError("FakeCatalogStorage::UpsertRoutine");
      99            0 :   }
     100            0 :   absl::Status DeleteRoutine(const storage::RoutineId&) override {
     101            0 :     return absl::UnimplementedError("FakeCatalogStorage::DeleteRoutine");
     102            0 :   }
     103              :   absl::StatusOr<storage::RoutineRecord> GetRoutine(
     104            0 :       const storage::RoutineId&) const override {
     105            0 :     return absl::UnimplementedError("FakeCatalogStorage::GetRoutine");
     106            0 :   }
     107              :   absl::StatusOr<std::vector<storage::RoutineRecord>> ListRoutines(
     108            3 :       const storage::DatasetId& dataset) const override {
     109            3 :     auto it = routines_.find(dataset.dataset_id);
     110            3 :     if (it == routines_.end()) {
     111            2 :       return std::vector<storage::RoutineRecord>{};
     112            2 :     }
     113            1 :     return it->second;
     114            3 :   }
     115              :   absl::StatusOr<std::vector<storage::RoutineRecord>> ListAllRoutines()
     116            0 :       const override {
     117            0 :     return absl::UnimplementedError("FakeCatalogStorage::ListAllRoutines");
     118            0 :   }
     119            0 :   absl::Status UpsertView(const storage::ViewRecord&) override {
     120            0 :     return absl::UnimplementedError("FakeCatalogStorage::UpsertView");
     121            0 :   }
     122            0 :   absl::Status DeleteView(const storage::ViewId&) override {
     123            0 :     return absl::UnimplementedError("FakeCatalogStorage::DeleteView");
     124            0 :   }
     125              :   absl::StatusOr<std::vector<storage::ViewRecord>> ListAllViews()
     126            0 :       const override {
     127            0 :     return absl::UnimplementedError("FakeCatalogStorage::ListAllViews");
     128            0 :   }
     129              : 
     130              :  private:
     131            4 :   static std::string TableKey(const storage::TableId& id) {
     132            4 :     return absl::StrCat(id.project_id, "/", id.dataset_id, "/", id.table_id);
     133            4 :   }
     134              : 
     135              :   std::map<std::string, std::vector<storage::DatasetId>> datasets_{};
     136              :   std::map<std::string, std::vector<storage::TableId>> tables_{};
     137              :   std::map<std::string, schema::TableSchema> schemas_{};
     138              :   std::map<std::string, std::vector<storage::RoutineRecord>> routines_{};
     139              : };
     140              : 
     141            1 : TEST(CatalogNamesTest, PopulateFromStorageIncludesTablesColumnsAndRoutines) {
     142            1 :   FakeCatalogStorage storage;
     143            1 :   storage.AddDataset("proj", "ds");
     144            1 :   storage.AddDataset("proj", "other");
     145              : 
     146            1 :   const storage::TableId table_id{"proj", "ds", "events"};
     147            1 :   schema::TableSchema schema;
     148            1 :   schema.columns = {
     149            1 :       schema::ColumnSchema{.name = "id", .type = schema::ColumnType::kInt64},
     150            1 :       schema::ColumnSchema{.name = "name", .type = schema::ColumnType::kString},
     151            1 :   };
     152            1 :   storage.AddTable(table_id, schema);
     153              : 
     154            1 :   storage::RoutineRecord routine;
     155            1 :   routine.id = storage::RoutineId{"proj", "ds", "my_fn"};
     156            1 :   routine.language = "SQL";
     157            1 :   storage.AddRoutine(storage::DatasetId{"proj", "ds"}, routine);
     158              : 
     159            1 :   CatalogNames names;
     160            1 :   ASSERT_TRUE(
     161            1 :       PopulateCatalogNamesFromStorage("proj", "ds", &storage, &names).ok());
     162              : 
     163            1 :   ASSERT_EQ(names.datasets,
     164            1 :             (std::vector<std::string>{"ds", "proj.ds", "other", "proj.other"}));
     165              : 
     166            1 :   ASSERT_EQ(names.tables.size(), 3u);
     167            1 :   EXPECT_EQ(names.tables[0].label, "ds.events");
     168            1 :   EXPECT_EQ(names.tables[0].fqn, "proj.ds.events");
     169            1 :   EXPECT_EQ(names.tables[0].kind, "table");
     170            1 :   EXPECT_EQ(names.tables[1].label, "proj.ds.events");
     171            1 :   EXPECT_EQ(names.tables[1].fqn, "proj.ds.events");
     172            1 :   EXPECT_EQ(names.tables[2].label, "events");
     173            1 :   EXPECT_EQ(names.tables[2].fqn, "proj.ds.events");
     174              : 
     175            1 :   ASSERT_EQ(names.routines.size(), 3u);
     176            1 :   EXPECT_EQ(names.routines[0].label, "ds.my_fn");
     177            1 :   EXPECT_EQ(names.routines[0].detail, "SQL scalar function");
     178            1 :   EXPECT_EQ(names.routines[0].kind, "routine");
     179            1 :   EXPECT_EQ(names.routines[1].label, "proj.ds.my_fn");
     180            1 :   EXPECT_EQ(names.routines[2].label, "my_fn");
     181              : 
     182            1 :   ASSERT_EQ(names.columns.size(), 2u);
     183            1 :   EXPECT_EQ(names.columns[0].name, "id");
     184            1 :   EXPECT_EQ(names.columns[0].type, "INT64");
     185            1 :   EXPECT_EQ(names.columns[1].name, "name");
     186            1 :   EXPECT_EQ(names.columns[1].type, "STRING");
     187              : 
     188            1 :   const auto qualified = names.columns_by_table.find("ds.events");
     189            1 :   ASSERT_NE(qualified, names.columns_by_table.end());
     190            1 :   EXPECT_EQ(qualified->second.size(), 2u);
     191              : 
     192            1 :   const auto unqualified = names.columns_by_table.find("events");
     193            1 :   ASSERT_NE(unqualified, names.columns_by_table.end());
     194            1 :   EXPECT_EQ(unqualified->second.size(), 2u);
     195            1 : }
     196              : 
     197            1 : TEST(CatalogNamesTest, PopulateFromStorageIncludesProjectQualifiedNames) {
     198            1 :   FakeCatalogStorage storage;
     199            1 :   storage.AddDataset("proj", "ds");
     200              : 
     201            1 :   const storage::TableId table_id{"proj", "ds", "events"};
     202            1 :   schema::TableSchema schema;
     203            1 :   schema.columns = {
     204            1 :       schema::ColumnSchema{.name = "id", .type = schema::ColumnType::kInt64},
     205            1 :   };
     206            1 :   storage.AddTable(table_id, schema);
     207              : 
     208            1 :   CatalogNames names;
     209            1 :   ASSERT_TRUE(
     210            1 :       PopulateCatalogNamesFromStorage("proj", "", &storage, &names).ok());
     211              : 
     212            1 :   bool found_fqn_table = false;
     213            1 :   bool found_fqn_dataset = false;
     214            2 :   for (const CatalogTableEntry& table : names.tables) {
     215            2 :     if (table.label == "proj.ds.events") {
     216            1 :       found_fqn_table = true;
     217            1 :     }
     218            2 :   }
     219            2 :   for (const std::string& dataset : names.datasets) {
     220            2 :     if (dataset == "proj.ds") {
     221            1 :       found_fqn_dataset = true;
     222            1 :     }
     223            2 :   }
     224            1 :   EXPECT_TRUE(found_fqn_table);
     225            1 :   EXPECT_TRUE(found_fqn_dataset);
     226            1 : }
     227              : 
     228            1 : TEST(CatalogNamesTest, RejectsNullStorage) {
     229            1 :   CatalogNames names;
     230            1 :   EXPECT_FALSE(
     231            1 :       PopulateCatalogNamesFromStorage("proj", "ds", nullptr, &names).ok());
     232            1 : }
     233              : 
     234            1 : TEST(CatalogNamesTest, RejectsNullNames) {
     235            1 :   FakeCatalogStorage storage;
     236            1 :   storage.AddDataset("proj", "ds");
     237            1 :   EXPECT_FALSE(
     238            1 :       PopulateCatalogNamesFromStorage("proj", "ds", &storage, nullptr).ok());
     239            1 : }
     240              : 
     241              : }  // namespace
     242              : }  // namespace sqltools
     243              : }  // namespace backend
     244              : }  // namespace bigquery_emulator
        

Generated by: LCOV version 2.0-1