Repository: apache/iceberg-go
Pull request #254 (2025-01-15 10:33:06)

Added Unit tests for Remaining Catalog operations for REST catalog
Part of #63 . Added For Create, Drop and Rename operations


Patch:
diff --git a/go.mod b/go.mod
index e91fe0b..e18c4ee 100644
--- a/go.mod
+++ b/go.mod
@@ -24,8 +24,8 @@ toolchain go1.23.2
 require (
 	github.com/apache/arrow-go/v18 v18.1.0
 	github.com/aws/aws-sdk-go-v2 v1.32.8
-	github.com/aws/aws-sdk-go-v2/config v1.28.10
-	github.com/aws/aws-sdk-go-v2/credentials v1.17.51
+	github.com/aws/aws-sdk-go-v2/config v1.28.11
+	github.com/aws/aws-sdk-go-v2/credentials v1.17.52
 	github.com/aws/aws-sdk-go-v2/service/glue v1.105.1
 	github.com/aws/aws-sdk-go-v2/service/s3 v1.72.2
 	github.com/aws/smithy-go v1.22.1
@@ -70,7 +70,7 @@ require (
 	github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.8 // indirect
 	github.com/aws/aws-sdk-go-v2/service/sso v1.24.9 // indirect
 	github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.8 // indirect
-	github.com/aws/aws-sdk-go-v2/service/sts v1.33.6 // indirect
+	github.com/aws/aws-sdk-go-v2/service/sts v1.33.7 // indirect
 	github.com/containerd/console v1.0.3 // indirect
 	github.com/creasty/defaults v1.8.0 // indirect
 	github.com/davecgh/go-spew v1.1.1 // indirect
diff --git a/go.sum b/go.sum
index dfcd01d..2ae76bb 100644
--- a/go.sum
+++ b/go.sum
@@ -50,8 +50,12 @@ github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 h1:lL7IfaFzngfx0ZwU
 github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7/go.mod h1:QraP0UcVlQJsmHfioCrveWOC1nbiWUl3ej08h4mXWoc=
 github.com/aws/aws-sdk-go-v2/config v1.28.10 h1:fKODZHfqQu06pCzR69KJ3GuttraRJkhlC8g80RZ0Dfg=
 github.com/aws/aws-sdk-go-v2/config v1.28.10/go.mod h1:PvdxRYZ5Um9QMq9PQ0zHHNdtKK+he2NHtFCUFMXWXeg=
+github.com/aws/aws-sdk-go-v2/config v1.28.11 h1:7Ekru0IkRHRnSRWGQLnLN6i0o1Jncd0rHo2T130+tEQ=
+github.com/aws/aws-sdk-go-v2/config v1.28.11/go.mod h1:x78TpPvBfHH16hi5tE3OCWQ0pzNfyXA349p5/Wp82Yo=
 github.com/aws/aws-sdk-go-v2/credentials v1.17.51 h1:F/9Sm6Y6k4LqDesZDPJCLxQGXNNHd/ZtJiWd0lCZKRk=
 github.com/aws/aws-sdk-go-v2/credentials v1.17.51/go.mod h1:TKbzCHm43AoPyA+iLGGcruXd4AFhF8tOmLex2R9jWNQ=
+github.com/aws/aws-sdk-go-v2/credentials v1.17.52 h1:I4ymSk35LHogx2Re2Wu6LOHNTRaRWkLVoJgWS5Wd40M=
+github.com/aws/aws-sdk-go-v2/credentials v1.17.52/go.mod h1:vAkqKbMNUcher8fDXP2Ge2qFXKMkcD74qvk1lJRMemM=
 github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.23 h1:IBAoD/1d8A8/1aA8g4MBVtTRHhXRiNAgwdbo/xRM2DI=
 github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.23/go.mod h1:vfENuCM7dofkgKpYzuzf1VT1UKkA/YL3qanfBn7HCaA=
 github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.11 h1:FEDZD/Axt5tKSkPAs967KZ++MkvYdBqr0a+cetRbjLM=
@@ -82,6 +86,8 @@ github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.8 h1:6dBT1Lz8fK11m22R+AqfRsFn
 github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.8/go.mod h1:/kiBvRQXBc6xeJTYzhSdGvJ5vm1tjaDEjH+MSeRJnlY=
 github.com/aws/aws-sdk-go-v2/service/sts v1.33.6 h1:VwhTrsTuVn52an4mXx29PqRzs2Dvu921NpGk7y43tAM=
 github.com/aws/aws-sdk-go-v2/service/sts v1.33.6/go.mod h1:+8h7PZb3yY5ftmVLD7ocEoE98hdc8PoKS0H3wfx1dlc=
+github.com/aws/aws-sdk-go-v2/service/sts v1.33.7 h1:qwGa9MA8G7mBq2YphHFaygdPe5t9OA7SvaJdwWTlEds=
+github.com/aws/aws-sdk-go-v2/service/sts v1.33.7/go.mod h1:+8h7PZb3yY5ftmVLD7ocEoE98hdc8PoKS0H3wfx1dlc=
 github.com/aws/smithy-go v1.22.1 h1:/HPHZQ0g7f4eUeK6HKglFz8uwVfZKgoI25rb/J+dnro=
 github.com/aws/smithy-go v1.22.1/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=


Test patch:
diff --git a/catalog/rest_test.go b/catalog/rest_test.go
index f290722..81ebbaf 100644
--- a/catalog/rest_test.go
+++ b/catalog/rest_test.go
@@ -757,6 +757,44 @@ func (r *RestCatalogSuite) TestCreateTable200() {
 	r.Equal(table.UnsortedSortOrder, tbl.SortOrder())
 }
 
+func (r *RestCatalogSuite) TestCreateTable409() {
+    // Mock the create table endpoint with 409 response
+    r.mux.HandleFunc("/v1/namespaces/fokko/tables", func(w http.ResponseWriter, req *http.Request) {
+        r.Require().Equal(http.MethodPost, req.Method)
+
+        for k, v := range TestHeaders {
+            r.Equal(v, req.Header.Values(k))
+        }
+
+        w.WriteHeader(http.StatusConflict)
+        errorResponse := map[string]interface{}{
+            "error": map[string]interface{}{
+                "message": "Table already exists: fokko.already_exists in warehouse 8bcb0838-50fc-472d-9ddb-8feb89ef5f1e",
+                "type":    "AlreadyExistsException",
+                "code":    409,
+            },
+        }
+        json.NewEncoder(w).Encode(errorResponse)
+    })
+
+    cat, err := catalog.NewRestCatalog("rest", r.srv.URL, catalog.WithOAuthToken(TestToken))
+    r.Require().NoError(err)
+
+    // Attempt to create table with properties
+    _, err = cat.CreateTable(
+        context.Background(),
+        catalog.ToRestIdentifier("fokko", "fokko2"),
+        tableSchemaSimple,
+        catalog.WithProperties(map[string]string{"owner": "fokko"}),
+    )
+
+    // Verify error
+    r.Error(err)
+    r.Contains(err.Error(), "Table already exists")
+    r.ErrorIs(err, catalog.ErrTableAlreadyExists)
+}
+
+
 func (r *RestCatalogSuite) TestLoadTable200() {
 	r.mux.HandleFunc("/v1/namespaces/fokko/tables/table", func(w http.ResponseWriter, req *http.Request) {
 		r.Require().Equal(http.MethodGet, req.Method)
@@ -876,6 +914,116 @@ func (r *RestCatalogSuite) TestLoadTable200() {
 	}))
 }
 
+func (r *RestCatalogSuite) TestRenameTable200() {
+    // Mock the rename table endpoint
+    r.mux.HandleFunc("/v1/tables/rename", func(w http.ResponseWriter, req *http.Request) {
+        r.Require().Equal(http.MethodPost, req.Method)
+
+        for k, v := range TestHeaders {
+            r.Equal(v, req.Header.Values(k))
+        }
+
+        var payload struct {
+            From struct {
+                Namespace []string `json:"namespace"`
+                Name     string   `json:"name"`
+            } `json:"from"`
+            To struct {
+                Namespace []string `json:"namespace"`
+                Name     string   `json:"name"`
+            } `json:"to"`
+        }
+        r.NoError(json.NewDecoder(req.Body).Decode(&payload))
+        r.Equal([]string{"fokko"}, payload.From.Namespace)
+        r.Equal("source", payload.From.Name)
+        r.Equal([]string{"fokko"}, payload.To.Namespace)
+        r.Equal("destination", payload.To.Name)
+
+        w.WriteHeader(http.StatusOK)
+    })
+
+    // Mock the get table endpoint for loading the renamed table
+    r.mux.HandleFunc("/v1/namespaces/fokko/tables/destination", func(w http.ResponseWriter, req *http.Request) {
+        r.Require().Equal(http.MethodGet, req.Method)
+
+        for k, v := range TestHeaders {
+            r.Equal(v, req.Header.Values(k))
+        }
+
+        w.Write([]byte(createTableRestExample))
+    })
+
+    cat, err := catalog.NewRestCatalog("rest", r.srv.URL, catalog.WithOAuthToken(TestToken))
+    r.Require().NoError(err)
+
+    fromIdent := catalog.ToRestIdentifier("fokko", "source")
+    toIdent := catalog.ToRestIdentifier("fokko", "destination")
+
+    renamedTable, err := cat.RenameTable(context.Background(), fromIdent, toIdent)
+    r.Require().NoError(err)
+
+    r.Equal(catalog.ToRestIdentifier("rest", "fokko", "destination"), renamedTable.Identifier())
+    r.Equal("s3://warehouse/database/table/metadata.json", renamedTable.MetadataLocation())
+    r.EqualValues(1, renamedTable.Metadata().Version())
+    r.Equal("bf289591-dcc0-4234-ad4f-5c3eed811a29", renamedTable.Metadata().TableUUID().String())
+    r.EqualValues(1657810967051, renamedTable.Metadata().LastUpdatedMillis())
+    r.Equal(3, renamedTable.Metadata().LastColumnID())
+    r.Zero(renamedTable.Schema().ID)
+    r.Zero(renamedTable.Metadata().DefaultPartitionSpec())
+    r.Equal(999, *renamedTable.Metadata().LastPartitionSpecID())
+    r.Equal(table.UnsortedSortOrder, renamedTable.SortOrder())
+}
+
+
+func (r *RestCatalogSuite) TestDropTable204() {
+    // Mock the drop table endpoint
+    r.mux.HandleFunc("/v1/namespaces/fokko/tables/table", func(w http.ResponseWriter, req *http.Request) {
+        r.Require().Equal(http.MethodDelete, req.Method)
+
+        for k, v := range TestHeaders {
+            r.Equal(v, req.Header.Values(k))
+        }
+
+        // Return 204 No Content for successful deletion
+        w.WriteHeader(http.StatusNoContent)
+    })
+
+    cat, err := catalog.NewRestCatalog("rest", r.srv.URL, catalog.WithOAuthToken(TestToken))
+    r.Require().NoError(err)
+
+    err = cat.DropTable(context.Background(), catalog.ToRestIdentifier("fokko", "table"))
+    r.NoError(err)
+}
+
+func (r *RestCatalogSuite) TestDropTable404() {
+    // Mock the drop table endpoint with 404 response
+    r.mux.HandleFunc("/v1/namespaces/fokko/tables/table", func(w http.ResponseWriter, req *http.Request) {
+        r.Require().Equal(http.MethodDelete, req.Method)
+
+        for k, v := range TestHeaders {
+            r.Equal(v, req.Header.Values(k))
+        }
+
+        w.WriteHeader(http.StatusNotFound)
+        errorResponse := map[string]interface{}{
+            "error": map[string]interface{}{
+                "message": "Table does not exist: fokko.table",
+                "type":    "NoSuchTableException",
+                "code":    404,
+            },
+        }
+        json.NewEncoder(w).Encode(errorResponse)
+    })
+
+    cat, err := catalog.NewRestCatalog("rest", r.srv.URL, catalog.WithOAuthToken(TestToken))
+    r.Require().NoError(err)
+
+    err = cat.DropTable(context.Background(), catalog.ToRestIdentifier("fokko", "table"))
+    r.Error(err)
+    r.ErrorIs(err, catalog.ErrNoSuchTable)
+    r.ErrorContains(err, "Table does not exist: fokko.table")
+}
+
 type RestTLSCatalogSuite struct {
 	suite.Suite