| /* | |
| * Licensed to the Apache Software Foundation (ASF) under one | |
| * or more contributor license agreements. See the NOTICE file | |
| * distributed with this work for additional information | |
| * regarding copyright ownership. The ASF licenses this file | |
| * to you under the Apache License, Version 2.0 (the | |
| * "License"); you may not use this file except in compliance | |
| * with the License. You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, | |
| * software distributed under the License is distributed on an | |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
| * KIND, either express or implied. See the License for the | |
| * specific language governing permissions and limitations | |
| * under the License. | |
| */ | |
| #include "iceberg/catalog/rest/endpoint.h" | |
| #include <gtest/gtest.h> | |
| #include <nlohmann/json.hpp> | |
| #include "iceberg/test/matchers.h" | |
| namespace iceberg::rest { | |
| TEST(EndpointTest, InvalidCreate) { | |
| // Empty path template should fail | |
| auto result = Endpoint::Make(HttpMethod::kGet, ""); | |
| EXPECT_THAT(result, IsError(ErrorKind::kInvalidArgument)); | |
| EXPECT_THAT(result, HasErrorMessage("Endpoint cannot have empty path")); | |
| } | |
| TEST(EndpointTest, ValidFromString) { | |
| auto result = Endpoint::FromString("GET /path"); | |
| EXPECT_THAT(result, IsOk()); | |
| auto endpoint = result.value(); | |
| EXPECT_EQ(endpoint.method(), HttpMethod::kGet); | |
| EXPECT_EQ(endpoint.path(), "/path"); | |
| } | |
| // Test all HTTP methods | |
| TEST(EndpointTest, AllHttpMethods) { | |
| auto get = Endpoint::Make(HttpMethod::kGet, "/path"); | |
| ASSERT_THAT(get, IsOk()); | |
| EXPECT_EQ(get->ToString(), "GET /path"); | |
| auto post = Endpoint::Make(HttpMethod::kPost, "/path"); | |
| ASSERT_THAT(post, IsOk()); | |
| EXPECT_EQ(post->ToString(), "POST /path"); | |
| auto put = Endpoint::Make(HttpMethod::kPut, "/path"); | |
| ASSERT_THAT(put, IsOk()); | |
| EXPECT_EQ(put->ToString(), "PUT /path"); | |
| auto del = Endpoint::Make(HttpMethod::kDelete, "/path"); | |
| ASSERT_THAT(del, IsOk()); | |
| EXPECT_EQ(del->ToString(), "DELETE /path"); | |
| auto head = Endpoint::Make(HttpMethod::kHead, "/path"); | |
| ASSERT_THAT(head, IsOk()); | |
| EXPECT_EQ(head->ToString(), "HEAD /path"); | |
| } | |
| // Test predefined namespace endpoints | |
| TEST(EndpointTest, NamespaceEndpoints) { | |
| auto list_namespaces = Endpoint::ListNamespaces(); | |
| EXPECT_EQ(list_namespaces.method(), HttpMethod::kGet); | |
| EXPECT_EQ(list_namespaces.path(), "/v1/{prefix}/namespaces"); | |
| EXPECT_EQ(list_namespaces.ToString(), "GET /v1/{prefix}/namespaces"); | |
| auto get_namespace = Endpoint::GetNamespaceProperties(); | |
| EXPECT_EQ(get_namespace.method(), HttpMethod::kGet); | |
| EXPECT_EQ(get_namespace.path(), "/v1/{prefix}/namespaces/{namespace}"); | |
| auto namespace_exists = Endpoint::NamespaceExists(); | |
| EXPECT_EQ(namespace_exists.method(), HttpMethod::kHead); | |
| EXPECT_EQ(namespace_exists.path(), "/v1/{prefix}/namespaces/{namespace}"); | |
| auto create_namespace = Endpoint::CreateNamespace(); | |
| EXPECT_EQ(create_namespace.method(), HttpMethod::kPost); | |
| EXPECT_EQ(create_namespace.path(), "/v1/{prefix}/namespaces"); | |
| auto update_namespace = Endpoint::UpdateNamespace(); | |
| EXPECT_EQ(update_namespace.method(), HttpMethod::kPost); | |
| EXPECT_EQ(update_namespace.path(), "/v1/{prefix}/namespaces/{namespace}/properties"); | |
| auto drop_namespace = Endpoint::DropNamespace(); | |
| EXPECT_EQ(drop_namespace.method(), HttpMethod::kDelete); | |
| EXPECT_EQ(drop_namespace.path(), "/v1/{prefix}/namespaces/{namespace}"); | |
| } | |
| // Test predefined table endpoints | |
| TEST(EndpointTest, TableEndpoints) { | |
| auto list_tables = Endpoint::ListTables(); | |
| EXPECT_EQ(list_tables.method(), HttpMethod::kGet); | |
| EXPECT_EQ(list_tables.path(), "/v1/{prefix}/namespaces/{namespace}/tables"); | |
| auto load_table = Endpoint::LoadTable(); | |
| EXPECT_EQ(load_table.method(), HttpMethod::kGet); | |
| EXPECT_EQ(load_table.path(), "/v1/{prefix}/namespaces/{namespace}/tables/{table}"); | |
| auto table_exists = Endpoint::TableExists(); | |
| EXPECT_EQ(table_exists.method(), HttpMethod::kHead); | |
| EXPECT_EQ(table_exists.path(), "/v1/{prefix}/namespaces/{namespace}/tables/{table}"); | |
| auto create_table = Endpoint::CreateTable(); | |
| EXPECT_EQ(create_table.method(), HttpMethod::kPost); | |
| EXPECT_EQ(create_table.path(), "/v1/{prefix}/namespaces/{namespace}/tables"); | |
| auto update_table = Endpoint::UpdateTable(); | |
| EXPECT_EQ(update_table.method(), HttpMethod::kPost); | |
| EXPECT_EQ(update_table.path(), "/v1/{prefix}/namespaces/{namespace}/tables/{table}"); | |
| auto delete_table = Endpoint::DeleteTable(); | |
| EXPECT_EQ(delete_table.method(), HttpMethod::kDelete); | |
| EXPECT_EQ(delete_table.path(), "/v1/{prefix}/namespaces/{namespace}/tables/{table}"); | |
| auto rename_table = Endpoint::RenameTable(); | |
| EXPECT_EQ(rename_table.method(), HttpMethod::kPost); | |
| EXPECT_EQ(rename_table.path(), "/v1/{prefix}/tables/rename"); | |
| auto register_table = Endpoint::RegisterTable(); | |
| EXPECT_EQ(register_table.method(), HttpMethod::kPost); | |
| EXPECT_EQ(register_table.path(), "/v1/{prefix}/namespaces/{namespace}/register"); | |
| auto report_metrics = Endpoint::ReportMetrics(); | |
| EXPECT_EQ(report_metrics.method(), HttpMethod::kPost); | |
| EXPECT_EQ(report_metrics.path(), | |
| "/v1/{prefix}/namespaces/{namespace}/tables/{table}/metrics"); | |
| auto table_credentials = Endpoint::TableCredentials(); | |
| EXPECT_EQ(table_credentials.method(), HttpMethod::kGet); | |
| EXPECT_EQ(table_credentials.path(), | |
| "/v1/{prefix}/namespaces/{namespace}/tables/{table}/credentials"); | |
| } | |
| // Test predefined transaction endpoints | |
| TEST(EndpointTest, TransactionEndpoints) { | |
| auto commit_transaction = Endpoint::CommitTransaction(); | |
| EXPECT_EQ(commit_transaction.method(), HttpMethod::kPost); | |
| EXPECT_EQ(commit_transaction.path(), "/v1/{prefix}/transactions/commit"); | |
| } | |
| // Test endpoint equality | |
| TEST(EndpointTest, Equality) { | |
| auto endpoint1 = Endpoint::Make(HttpMethod::kGet, "/path"); | |
| auto endpoint2 = Endpoint::Make(HttpMethod::kGet, "/path"); | |
| auto endpoint3 = Endpoint::Make(HttpMethod::kPost, "/path"); | |
| auto endpoint4 = Endpoint::Make(HttpMethod::kGet, "/other"); | |
| ASSERT_THAT(endpoint1, IsOk()); | |
| ASSERT_THAT(endpoint2, IsOk()); | |
| ASSERT_THAT(endpoint3, IsOk()); | |
| ASSERT_THAT(endpoint4, IsOk()); | |
| // Equality | |
| EXPECT_EQ(*endpoint1, *endpoint2); | |
| EXPECT_NE(*endpoint1, *endpoint3); | |
| EXPECT_NE(*endpoint1, *endpoint4); | |
| } | |
| // Test string serialization | |
| TEST(EndpointTest, ToStringFormat) { | |
| auto endpoint1 = Endpoint::Make(HttpMethod::kGet, "/v1/{prefix}/namespaces"); | |
| ASSERT_THAT(endpoint1, IsOk()); | |
| EXPECT_EQ(endpoint1->ToString(), "GET /v1/{prefix}/namespaces"); | |
| auto endpoint2 = Endpoint::Make(HttpMethod::kPost, "/v1/{prefix}/tables"); | |
| ASSERT_THAT(endpoint2, IsOk()); | |
| EXPECT_EQ(endpoint2->ToString(), "POST /v1/{prefix}/tables"); | |
| // Test with all HTTP methods | |
| auto endpoint3 = Endpoint::Make(HttpMethod::kDelete, "/path"); | |
| ASSERT_THAT(endpoint3, IsOk()); | |
| EXPECT_EQ(endpoint3->ToString(), "DELETE /path"); | |
| auto endpoint4 = Endpoint::Make(HttpMethod::kPut, "/path"); | |
| ASSERT_THAT(endpoint4, IsOk()); | |
| EXPECT_EQ(endpoint4->ToString(), "PUT /path"); | |
| auto endpoint5 = Endpoint::Make(HttpMethod::kHead, "/path"); | |
| ASSERT_THAT(endpoint5, IsOk()); | |
| EXPECT_EQ(endpoint5->ToString(), "HEAD /path"); | |
| } | |
| // Test string deserialization | |
| TEST(EndpointTest, FromStringParsing) { | |
| auto result1 = Endpoint::FromString("GET /v1/{prefix}/namespaces"); | |
| ASSERT_THAT(result1, IsOk()); | |
| EXPECT_EQ(result1->method(), HttpMethod::kGet); | |
| EXPECT_EQ(result1->path(), "/v1/{prefix}/namespaces"); | |
| auto result2 = Endpoint::FromString("POST /v1/{prefix}/namespaces/{namespace}/tables"); | |
| ASSERT_THAT(result2, IsOk()); | |
| EXPECT_EQ(result2->method(), HttpMethod::kPost); | |
| EXPECT_EQ(result2->path(), "/v1/{prefix}/namespaces/{namespace}/tables"); | |
| // Test all HTTP methods | |
| auto result3 = Endpoint::FromString("DELETE /path"); | |
| ASSERT_THAT(result3, IsOk()); | |
| EXPECT_EQ(result3->method(), HttpMethod::kDelete); | |
| auto result4 = Endpoint::FromString("PUT /path"); | |
| ASSERT_THAT(result4, IsOk()); | |
| EXPECT_EQ(result4->method(), HttpMethod::kPut); | |
| auto result5 = Endpoint::FromString("HEAD /path"); | |
| ASSERT_THAT(result5, IsOk()); | |
| EXPECT_EQ(result5->method(), HttpMethod::kHead); | |
| } | |
| // Test string parsing with invalid inputs | |
| TEST(EndpointTest, FromStringInvalid) { | |
| // Invalid endpoint format should fail - missing space | |
| auto result1 = Endpoint::FromString("/path/without/method"); | |
| EXPECT_THAT(result1, IsError(ErrorKind::kInvalidArgument)); | |
| EXPECT_THAT(result1, | |
| HasErrorMessage("Invalid endpoint format (must consist of two elements " | |
| "separated by a single space)")); | |
| // Invalid HTTP method should fail | |
| auto result2 = Endpoint::FromString("INVALID /path"); | |
| EXPECT_THAT(result2, IsError(ErrorKind::kInvalidArgument)); | |
| EXPECT_THAT(result2, HasErrorMessage("Invalid HTTP method")); | |
| // Invalid endpoint format - extra element after path | |
| auto result3 = Endpoint::FromString("GET /path INVALID"); | |
| EXPECT_THAT(result3, IsError(ErrorKind::kInvalidArgument)); | |
| EXPECT_THAT(result3, | |
| HasErrorMessage("Invalid endpoint format (must consist of two elements " | |
| "separated by a single space)")); | |
| } | |
| // Test string round-trip | |
| TEST(EndpointTest, StringRoundTrip) { | |
| // Create various endpoints and verify they survive string round-trip | |
| std::vector<Endpoint> endpoints = { | |
| Endpoint::ListNamespaces(), Endpoint::GetNamespaceProperties(), | |
| Endpoint::CreateNamespace(), Endpoint::LoadTable(), | |
| Endpoint::CreateTable(), Endpoint::DeleteTable(), | |
| }; | |
| for (const auto& original : endpoints) { | |
| // Serialize to string | |
| std::string str = original.ToString(); | |
| // Deserialize from string | |
| auto deserialized = Endpoint::FromString(str); | |
| ASSERT_THAT(deserialized, IsOk()); | |
| // Verify they are equal | |
| EXPECT_EQ(original, *deserialized); | |
| EXPECT_EQ(original.ToString(), deserialized->ToString()); | |
| } | |
| } | |
| } // namespace iceberg::rest |