nastra commented on code in PR #8857:
URL: https://github.com/apache/iceberg/pull/8857#discussion_r1417126334


##########
nessie/src/test/java/org/apache/iceberg/nessie/TestMultipleClients.java:
##########
@@ -67,33 +71,84 @@ public void afterEach() throws Exception {
   }
 
   @Test
-  public void testListNamespaces() {
+  public void testListNamespaces() throws NessieConflictException, 
NessieNotFoundException {
+    Assertions.assertThat(catalog.listNamespaces()).isEmpty();
+    Assertions.assertThat(anotherCatalog.listNamespaces()).isEmpty();
+
+    // listing a non-existent namespace should return empty
+    
Assertions.assertThat(catalog.listNamespaces(Namespace.of("db1"))).isEmpty();
+    
Assertions.assertThat(anotherCatalog.listNamespaces(Namespace.of("db1"))).isEmpty();
+
     catalog.createNamespace(Namespace.of("db1"), Collections.emptyMap());
+
     
Assertions.assertThat(catalog.listNamespaces()).containsExactlyInAnyOrder(Namespace.of("db1"));
+    Assertions.assertThat(anotherCatalog.listNamespaces())
+        .containsExactlyInAnyOrder(Namespace.of("db1"));
 
     // another client creates a namespace with the same nessie server
     anotherCatalog.createNamespace(Namespace.of("db2"), 
Collections.emptyMap());
-    Assertions.assertThat(anotherCatalog.listNamespaces())
-        .containsExactlyInAnyOrder(Namespace.of("db1"), Namespace.of("db2"));
 
     Assertions.assertThat(catalog.listNamespaces())
         .containsExactlyInAnyOrder(Namespace.of("db1"), Namespace.of("db2"));
+    Assertions.assertThat(anotherCatalog.listNamespaces())
+        .containsExactlyInAnyOrder(Namespace.of("db1"), Namespace.of("db2"));
+
+    api.deleteBranch().branch((Branch) 
api.getReference().refName(branch).get()).delete();
+
+    Assertions.assertThatThrownBy(() -> catalog.listNamespaces())
+        .hasMessageContaining(

Review Comment:
   this should have a `isInstanceOf(...)`. Same for the assertion a few lines 
below



##########
nessie/src/test/java/org/apache/iceberg/nessie/TestMultipleClients.java:
##########
@@ -67,33 +71,84 @@ public void afterEach() throws Exception {
   }
 
   @Test
-  public void testListNamespaces() {
+  public void testListNamespaces() throws NessieConflictException, 
NessieNotFoundException {
+    Assertions.assertThat(catalog.listNamespaces()).isEmpty();
+    Assertions.assertThat(anotherCatalog.listNamespaces()).isEmpty();
+
+    // listing a non-existent namespace should return empty
+    
Assertions.assertThat(catalog.listNamespaces(Namespace.of("db1"))).isEmpty();
+    
Assertions.assertThat(anotherCatalog.listNamespaces(Namespace.of("db1"))).isEmpty();
+
     catalog.createNamespace(Namespace.of("db1"), Collections.emptyMap());
+
     
Assertions.assertThat(catalog.listNamespaces()).containsExactlyInAnyOrder(Namespace.of("db1"));
+    Assertions.assertThat(anotherCatalog.listNamespaces())
+        .containsExactlyInAnyOrder(Namespace.of("db1"));
 
     // another client creates a namespace with the same nessie server
     anotherCatalog.createNamespace(Namespace.of("db2"), 
Collections.emptyMap());
-    Assertions.assertThat(anotherCatalog.listNamespaces())
-        .containsExactlyInAnyOrder(Namespace.of("db1"), Namespace.of("db2"));
 
     Assertions.assertThat(catalog.listNamespaces())
         .containsExactlyInAnyOrder(Namespace.of("db1"), Namespace.of("db2"));
+    Assertions.assertThat(anotherCatalog.listNamespaces())
+        .containsExactlyInAnyOrder(Namespace.of("db1"), Namespace.of("db2"));
+
+    api.deleteBranch().branch((Branch) 
api.getReference().refName(branch).get()).delete();
+
+    Assertions.assertThatThrownBy(() -> catalog.listNamespaces())
+        .hasMessageContaining(
+            "Cannot list top-level namespaces: ref '%s' is no longer valid", 
branch);
+    Assertions.assertThatThrownBy(() -> 
anotherCatalog.listNamespaces(Namespace.of("db1")))
+        .hasMessageContaining(
+            "Cannot list child namespaces from 'db1': ref '%s' is no longer 
valid", branch);
   }
 
   @Test
-  public void testLoadNamespaceMetadata() {
+  public void testLoadNamespaceMetadata() throws NessieConflictException, 
NessieNotFoundException {
+
+    Assertions.assertThatThrownBy(() -> 
catalog.loadNamespaceMetadata(Namespace.of("namespace1")))
+        .isInstanceOf(NoSuchNamespaceException.class)
+        .hasMessageContaining("Namespace does not exist: namespace1");
+    Assertions.assertThatThrownBy(
+            () -> 
anotherCatalog.loadNamespaceMetadata(Namespace.of("namespace1")))
+        .isInstanceOf(NoSuchNamespaceException.class)
+        .hasMessageContaining("Namespace does not exist: namespace1");
+
     catalog.createNamespace(Namespace.of("namespace1"), 
Collections.emptyMap());
+
+    // both clients should see the namespace because we read the HEAD of the 
ref
     Assertions.assertThat(catalog.listNamespaces())
         .containsExactlyInAnyOrder(Namespace.of("namespace1"));
+    Assertions.assertThat(anotherCatalog.listNamespaces())
+        .containsExactlyInAnyOrder(Namespace.of("namespace1"));
 
-    // another client adds a metadata to the same namespace
-    anotherCatalog.setProperties(Namespace.of("namespace1"), 
Collections.singletonMap("k1", "v1"));
-    AbstractMap.SimpleEntry<String, String> entry = new 
AbstractMap.SimpleEntry<>("k1", "v1");
+    // the other client should not be able to update the namespace
+    // because it is still on the old ref hash
+    Assertions.assertThatThrownBy(
+            () ->
+                anotherCatalog.setProperties(
+                    Namespace.of("namespace1"), Collections.singletonMap("k1", 
"v1")))
+        .isInstanceOf(NoSuchNamespaceException.class)
+        .hasMessageContaining("Namespace does not exist: namespace1");
+    // the same client adds a metadata to the namespace: expect success
+    catalog.setProperties(Namespace.of("namespace1"), 
Collections.singletonMap("k1", "v1"));
+
+    // load metadata from the same client and another client both should work 
fine
+    // because we read the HEAD of the ref
     
Assertions.assertThat(anotherCatalog.loadNamespaceMetadata(Namespace.of("namespace1")))
-        .containsExactly(entry);
-
+        .containsExactly(Map.entry("k1", "v1"));
     
Assertions.assertThat(catalog.loadNamespaceMetadata(Namespace.of("namespace1")))
-        .containsExactly(entry);
+        .containsExactly(Map.entry("k1", "v1"));
+
+    api.deleteBranch().branch((Branch) 
api.getReference().refName(branch).get()).delete();
+
+    Assertions.assertThatThrownBy(() -> 
catalog.loadNamespaceMetadata(Namespace.of("namespace1")))
+        .hasMessageContaining(

Review Comment:
   this and the check below needs a `.isInstanceOf()` check



##########
nessie/src/test/java/org/apache/iceberg/nessie/TestNessieIcebergClient.java:
##########
@@ -91,15 +113,444 @@ public void testWithReferenceAfterRecreatingBranch()
     Assertions.assertThat(client.withReference(branch, 
null)).isNotEqualTo(client);
   }
 
+  @Test
+  public void testCreateNamespace() throws NessieConflictException, 
NessieNotFoundException {
+    String branch = "createNamespaceBranch";
+    createBranch(branch);
+    Map<String, String> catalogOptions =
+        Map.of(
+            CatalogProperties.USER, "iceberg-user",
+            CatalogProperties.APP_ID, "iceberg-nessie");
+    NessieIcebergClient client = new NessieIcebergClient(api, branch, null, 
catalogOptions);
+
+    client.createNamespace(Namespace.of("a"), Map.of());
+    
Assertions.assertThat(client.listNamespaces(Namespace.of("a"))).isNotNull();
+
+    List<LogResponse.LogEntry> entries = 
api.getCommitLog().refName(branch).get().getLogEntries();
+    Assertions.assertThat(entries)
+        .isNotEmpty()
+        .first()
+        .extracting(LogResponse.LogEntry::getCommitMeta)
+        .extracting(CommitMeta::getMessage, CommitMeta::getAuthor, 
CommitMeta::getProperties)
+        .containsExactly(
+            "create namespace a",
+            "iceberg-user",
+            ImmutableMap.of(
+                "application-type", "iceberg",
+                "app-id", "iceberg-nessie"));
+  }
+
+  @Test
+  public void testCreateNamespaceInvalid() throws NessieConflictException, 
NessieNotFoundException {
+    String branch = "createNamespaceInvalidBranch";
+    createBranch(branch);
+    NessieIcebergClient client = new NessieIcebergClient(api, branch, null, 
Map.of());
+
+    Assertions.assertThatIllegalArgumentException()
+        .isThrownBy(() -> client.createNamespace(Namespace.empty(), Map.of()))
+        .withMessageContaining("Creating empty namespaces is not supported");
+
+    Assertions.assertThatThrownBy(() -> 
client.createNamespace(Namespace.of("a", "b"), Map.of()))
+        .isInstanceOf(NoSuchNamespaceException.class)
+        .hasMessageContaining("Cannot create namespace 'a.b': parent namespace 
'a' does not exist");
+  }
+
+  @Test
+  public void testCreateNamespaceConflict()
+      throws NessieConflictException, NessieNotFoundException {
+    String branch = "createNamespaceConflictBranch";
+    createBranch(branch);
+    NessieIcebergClient client = new NessieIcebergClient(api, branch, null, 
Map.of());
+
+    client.createNamespace(Namespace.of("a"), Map.of());
+
+    Assertions.assertThatThrownBy(() -> 
client.createNamespace(Namespace.of("a"), Map.of()))
+        .isInstanceOf(AlreadyExistsException.class)
+        .hasMessageContaining("Namespace already exists: a");
+
+    client.commitTable(
+        null, newTable(), "file:///tmp/iceberg", (String) null, 
ContentKey.of("a", "tbl"));
+
+    Assertions.assertThatThrownBy(() -> 
client.createNamespace(Namespace.of("a", "tbl"), Map.of()))
+        .isInstanceOf(AlreadyExistsException.class)
+        .hasMessageContaining("Another content object with name 'a.tbl' 
already exists");
+  }
+
+  @Test
+  public void testCreateNamespaceExternalConflict()
+      throws NessieConflictException, NessieNotFoundException {
+    String branch = "createNamespaceExternalConflictBranch";
+    createBranch(branch);
+    NessieIcebergClient client = new NessieIcebergClient(api, branch, null, 
Map.of());
+
+    org.projectnessie.model.Namespace nessieNs =
+        org.projectnessie.model.Namespace.of(ContentKey.of("a"));
+    commit(branch, "create namespace a", Operation.Put.of(ContentKey.of("a"), 
nessieNs));
+
+    Assertions.assertThatThrownBy(() -> 
client.createNamespace(Namespace.of("a"), Map.of()))
+        .isInstanceOf(AlreadyExistsException.class)
+        .hasMessageContaining("Namespace already exists: a");
+
+    IcebergTable table = IcebergTable.of("file:///tmp/iceberg", 1, 1, 1, 1);
+    commit(branch, "create table a.tbl2", Operation.Put.of(ContentKey.of("a", 
"tbl"), table));
+
+    Assertions.assertThatThrownBy(() -> 
client.createNamespace(Namespace.of("a", "tbl"), Map.of()))
+        .isInstanceOf(AlreadyExistsException.class)
+        .hasMessageContaining("Another content object with name 'a.tbl' 
already exists");
+  }
+
+  @Test
+  public void testCreateNamespaceNonExistingRef()
+      throws NessieConflictException, NessieNotFoundException {
+    String branch = "createNamespaceNonExistingRefBranch";
+    createBranch(branch);
+    NessieIcebergClient client = new NessieIcebergClient(api, branch, null, 
Map.of());
+
+    client.createNamespace(Namespace.of("a"), Map.of());
+
+    api.deleteBranch().branch((Branch) 
api.getReference().refName(branch).get()).delete();
+
+    Assertions.assertThatThrownBy(() -> 
client.createNamespace(Namespace.of("b"), Map.of()))
+        .isInstanceOf(RuntimeException.class)
+        .hasMessageContaining(
+            "Cannot create namespace 'b': ref 
'createNamespaceNonExistingRefBranch' is no longer valid");
+  }
+
+  @Test
+  public void testDropNamespace() throws NessieConflictException, 
NessieNotFoundException {
+    String branch = "dropNamespaceBranch";
+    createBranch(branch);
+    Map<String, String> catalogOptions =
+        Map.of(
+            CatalogProperties.USER, "iceberg-user",
+            CatalogProperties.APP_ID, "iceberg-nessie");
+    NessieIcebergClient client = new NessieIcebergClient(api, branch, null, 
catalogOptions);
+
+    Namespace parent = Namespace.of("a");
+    Namespace child = Namespace.of("a", "b");
+
+    Assertions.assertThat(client.dropNamespace(parent)).isFalse();
+    Assertions.assertThat(client.dropNamespace(child)).isFalse();
+
+    client.createNamespace(parent, Map.of());
+    client.createNamespace(child, Map.of());
+
+    Assertions.assertThat(client.dropNamespace(child)).isTrue();
+    Assertions.assertThat(client.dropNamespace(parent)).isTrue();
+
+    List<LogResponse.LogEntry> entries = 
api.getCommitLog().refName(branch).get().getLogEntries();
+    Assertions.assertThat(entries)
+        .isNotEmpty()
+        .extracting(LogResponse.LogEntry::getCommitMeta)
+        .satisfies(
+            meta -> {
+              Assertions.assertThat(meta.getMessage()).contains("drop 
namespace a");
+              
Assertions.assertThat(meta.getAuthor()).isEqualTo("iceberg-user");
+              Assertions.assertThat(meta.getProperties())
+                  .containsEntry(NessieUtil.APPLICATION_TYPE, "iceberg")
+                  .containsEntry(CatalogProperties.APP_ID, "iceberg-nessie");
+            },
+            Index.atIndex(0))
+        .satisfies(
+            meta -> {
+              Assertions.assertThat(meta.getMessage()).contains("drop 
namespace a.b");
+              
Assertions.assertThat(meta.getAuthor()).isEqualTo("iceberg-user");
+              Assertions.assertThat(meta.getProperties())
+                  .containsEntry(NessieUtil.APPLICATION_TYPE, "iceberg")
+                  .containsEntry(CatalogProperties.APP_ID, "iceberg-nessie");
+            },
+            Index.atIndex(1));
+  }
+
+  @Test
+  public void testDropNamespaceNotEmpty() throws NessieConflictException, 
NessieNotFoundException {
+    String branch = "dropNamespaceInvalidBranch";
+    createBranch(branch);
+    NessieIcebergClient client = new NessieIcebergClient(api, branch, null, 
Map.of());
+
+    client.createNamespace(Namespace.of("a"), Map.of());
+    client.createNamespace(Namespace.of("a", "b"), Map.of());
+
+    Assertions.assertThatThrownBy(() -> 
client.dropNamespace(Namespace.of("a")))
+        .isInstanceOf(NamespaceNotEmptyException.class)
+        .hasMessageContaining("Namespace 'a' is not empty.");
+  }
+
+  @Test
+  public void testDropNamespaceConflict() throws NessieConflictException, 
NessieNotFoundException {
+    String branch = "dropNamespaceConflictBranch";
+    createBranch(branch);
+    NessieIcebergClient client = new NessieIcebergClient(api, branch, null, 
Map.of());
+
+    client.createNamespace(Namespace.of("a"), Map.of());
+
+    client.commitTable(
+        null, newTable(), "file:///tmp/iceberg", (String) null, 
ContentKey.of("a", "tbl"));
+
+    Assertions.assertThatThrownBy(() -> client.dropNamespace(Namespace.of("a", 
"tbl")))
+        .isInstanceOf(NoSuchNamespaceException.class)
+        .hasMessageContaining("Content object with name 'a.tbl' is not a 
namespace.");
+  }
+
+  @Test
+  public void testDropNamespaceExternalConflict()
+      throws NessieConflictException, NessieNotFoundException {
+    String branch = "dropNamespaceExternalConflictBranch";
+    createBranch(branch);
+    NessieIcebergClient client = new NessieIcebergClient(api, branch, null, 
Map.of());
+
+    client.createNamespace(Namespace.of("a"), Map.of());
+
+    org.projectnessie.model.Namespace original = 
fetchNamespace(ContentKey.of("a"), branch);
+    org.projectnessie.model.Namespace updated =
+        org.projectnessie.model.Namespace.builder()
+            .from(original)
+            .properties(Map.of("k1", "v1"))
+            .build();
+    commit(branch, "update namespace a", Operation.Put.of(ContentKey.of("a"), 
updated));
+
+    Assertions.assertThatThrownBy(() -> 
client.dropNamespace(Namespace.of("a")))
+        .isInstanceOf(RuntimeException.class)
+        .hasMessageContaining(
+            "Cannot drop namespace 'a': Values of existing and expected 
content for key 'a' are different.");
+  }
+
+  @Test
+  public void testDropNamespaceNonExistingRef()
+      throws NessieConflictException, NessieNotFoundException {
+    String branch = "dropNamespaceNonExistingRefBranch";
+    createBranch(branch);
+    NessieIcebergClient client = new NessieIcebergClient(api, branch, null, 
Map.of());
+
+    client.createNamespace(Namespace.of("a"), Map.of());
+
+    api.deleteBranch().branch((Branch) 
api.getReference().refName(branch).get()).delete();
+
+    Assertions.assertThat(client.dropNamespace(Namespace.of("a"))).isFalse();
+  }
+
+  @Test
+  public void testSetProperties() throws NessieConflictException, 
NessieNotFoundException {
+    String branch = "setPropertiesBranch";
+    createBranch(branch);
+    Map<String, String> catalogOptions =
+        Map.of(
+            CatalogProperties.USER, "iceberg-user",
+            CatalogProperties.APP_ID, "iceberg-nessie");
+    NessieIcebergClient client = new NessieIcebergClient(api, branch, null, 
catalogOptions);
+
+    Namespace ns = Namespace.of("a");
+    client.createNamespace(ns, Map.of("k1", "v1a"));
+
+    Assertions.assertThat(client.setProperties(ns, Map.of("k1", "v1b", "k2", 
"v2"))).isTrue();
+
+    Assertions.assertThat(client.loadNamespaceMetadata(ns))
+        .hasSize(2)
+        .containsEntry("k1", "v1b")
+        .containsEntry("k2", "v2");
+
+    List<LogResponse.LogEntry> entries = 
api.getCommitLog().refName(branch).get().getLogEntries();
+    Assertions.assertThat(entries)
+        .isNotEmpty()
+        .first()
+        .extracting(LogResponse.LogEntry::getCommitMeta)
+        .satisfies(
+            meta -> {
+              Assertions.assertThat(meta.getMessage()).contains("update 
namespace a");
+              
Assertions.assertThat(meta.getAuthor()).isEqualTo("iceberg-user");
+              Assertions.assertThat(meta.getProperties())
+                  .containsEntry(NessieUtil.APPLICATION_TYPE, "iceberg")
+                  .containsEntry(CatalogProperties.APP_ID, "iceberg-nessie");
+            });
+  }
+
+  @Test
+  public void testSetPropertiesExternalConflict()
+      throws NessieConflictException, NessieNotFoundException {
+    String branch = "setPropertiesExternalConflictBranch";
+    createBranch(branch);
+    NessieIcebergClient client = new NessieIcebergClient(api, branch, null, 
Map.of());
+
+    Namespace ns = Namespace.of("a");
+    client.createNamespace(ns, Map.of("k1", "v1a"));
+
+    ContentKey key = ContentKey.of("a");
+    org.projectnessie.model.Namespace original = fetchNamespace(key, branch);
+    org.projectnessie.model.Namespace updated =
+        org.projectnessie.model.Namespace.builder()
+            .from(original)
+            .properties(Map.of("k1", "v1b", "k2", "v2"))
+            .build();
+    commit(branch, "update namespace a", Operation.Put.of(key, updated));
+
+    // will generate a conflict and a retry
+    Assertions.assertThat(client.setProperties(ns, Map.of("k1", "v1c", "k3", 
"v3"))).isTrue();
+
+    Assertions.assertThat(client.loadNamespaceMetadata(ns))
+        .hasSize(3)
+        .containsEntry("k1", "v1c")
+        .containsEntry("k2", "v2")
+        .containsEntry("k3", "v3");
+  }
+
+  @Test
+  public void testSetPropertiesNonExistingNs()
+      throws NessieConflictException, NessieNotFoundException {
+    String branch = "setPropertiesNonExistingNsBranch";
+    createBranch(branch);
+    NessieIcebergClient client = new NessieIcebergClient(api, branch, null, 
Map.of());
+
+    client.createNamespace(Namespace.of("a"), Map.of());
+
+    commit(branch, "delete namespace a", 
Operation.Delete.of(ContentKey.of("a")));
+
+    Assertions.assertThatThrownBy(
+            () -> client.setProperties(Namespace.of("a"), Map.of("k1", "v1a")))
+        .isInstanceOf(NoSuchNamespaceException.class)
+        .hasMessageContaining("Namespace does not exist: a");
+  }
+
+  @Test
+  public void testSetPropertiesNonExistingRef()
+      throws NessieConflictException, NessieNotFoundException {
+    String branch = "setPropertiesNonExistingRefBranch";
+    createBranch(branch);
+    NessieIcebergClient client = new NessieIcebergClient(api, branch, null, 
Map.of());
+
+    client.createNamespace(Namespace.of("a"), Map.of());
+
+    api.deleteBranch().branch((Branch) 
api.getReference().refName(branch).get()).delete();
+
+    Assertions.assertThatThrownBy(() -> 
client.setProperties(Namespace.of("a"), Map.of("k1", "v1")))
+        .isInstanceOf(RuntimeException.class)
+        .hasMessageContaining(
+            "Cannot update properties on namespace 'a': ref 
'setPropertiesNonExistingRefBranch' is no longer valid");
+  }
+
+  @Test
+  public void testRemoveProperties() throws NessieConflictException, 
NessieNotFoundException {
+    String branch = "removePropertiesBranch";
+    createBranch(branch);
+    Map<String, String> catalogOptions =
+        Map.of(
+            CatalogProperties.USER, "iceberg-user",
+            CatalogProperties.APP_ID, "iceberg-nessie");
+    NessieIcebergClient client = new NessieIcebergClient(api, branch, null, 
catalogOptions);
+
+    Namespace ns = Namespace.of("a");
+
+    client.createNamespace(ns, Map.of("k1", "v1", "k2", "v2"));
+
+    Assertions.assertThat(client.removeProperties(ns, Set.of("k1"))).isTrue();
+
+    
Assertions.assertThat(client.loadNamespaceMetadata(ns)).hasSize(1).containsOnlyKeys("k2");
+
+    List<LogResponse.LogEntry> entries = 
api.getCommitLog().refName(branch).get().getLogEntries();
+    Assertions.assertThat(entries)
+        .isNotEmpty()
+        .first()
+        .extracting(LogResponse.LogEntry::getCommitMeta)
+        .satisfies(
+            meta -> {
+              Assertions.assertThat(meta.getMessage()).contains("update 
namespace a");
+              
Assertions.assertThat(meta.getAuthor()).isEqualTo("iceberg-user");
+              Assertions.assertThat(meta.getProperties())
+                  .containsEntry(NessieUtil.APPLICATION_TYPE, "iceberg")
+                  .containsEntry(CatalogProperties.APP_ID, "iceberg-nessie");
+            });
+  }
+
+  @Test
+  public void testRemovePropertiesExternalConflict()
+      throws NessieConflictException, NessieNotFoundException {
+    String branch = "removePropertiesExternalConflictBranch";
+    createBranch(branch);
+    NessieIcebergClient client = new NessieIcebergClient(api, branch, null, 
Map.of());
+
+    Namespace ns = Namespace.of("a");
+    client.createNamespace(ns, Map.of("k1", "v1"));
+
+    ContentKey key = ContentKey.of("a");
+    org.projectnessie.model.Namespace original = fetchNamespace(key, branch);
+    org.projectnessie.model.Namespace updated =
+        org.projectnessie.model.Namespace.builder()
+            .from(original)
+            .properties(Map.of("k2", "v2", "k3", "v3"))
+            .build();
+    commit(branch, "update namespace a", Operation.Put.of(key, updated));
+
+    // will generate a conflict and a retry
+    Assertions.assertThat(client.removeProperties(ns, Set.of("k2"))).isTrue();
+
+    
Assertions.assertThat(client.loadNamespaceMetadata(ns)).hasSize(1).containsOnlyKeys("k3");
+  }
+
+  @Test
+  public void testRemovePropertiesNonExistingNs()
+      throws NessieConflictException, NessieNotFoundException {
+    String branch = "removePropertiesNonExistingNsBranch";
+    createBranch(branch);
+    NessieIcebergClient client = new NessieIcebergClient(api, branch, null, 
Map.of());
+
+    client.createNamespace(Namespace.of("a"), Map.of("k1", "v1"));
+
+    commit(branch, "delete namespace a", 
Operation.Delete.of(ContentKey.of("a")));
+
+    Assertions.assertThatThrownBy(() -> 
client.removeProperties(Namespace.of("a"), Set.of("k1")))
+        .isInstanceOf(NoSuchNamespaceException.class)
+        .hasMessageContaining("Namespace does not exist: a");
+  }
+
+  @Test
+  public void testRemovePropertiesNonExistingRef()
+      throws NessieConflictException, NessieNotFoundException {
+    String branch = "removePropertiesNonExistingRefBranch";
+    createBranch(branch);
+    NessieIcebergClient client = new NessieIcebergClient(api, branch, null, 
Map.of());
+
+    client.createNamespace(Namespace.of("a"), Map.of("k1", "v1"));
+
+    api.deleteBranch().branch((Branch) 
api.getReference().refName(branch).get()).delete();
+
+    Assertions.assertThatThrownBy(() -> 
client.removeProperties(Namespace.of("a"), Set.of("k1")))
+        .isInstanceOf(RuntimeException.class)
+        .hasMessageContaining(
+            "Cannot update properties on namespace 'a': ref 
'removePropertiesNonExistingRefBranch' is no longer valid");
+  }
+
   @Test
   public void testInvalidClientApiVersion() throws IOException {
     try (NessieCatalog newCatalog = new NessieCatalog()) {
       newCatalog.setConf(hadoopConfig);
       ImmutableMap.Builder<String, String> options =
           ImmutableMap.<String, String>builder().put("client-api-version", 
"3");
-      Assertions.assertThatThrownBy(() -> newCatalog.initialize("nessie", 
options.buildOrThrow()))
-          .isInstanceOf(IllegalArgumentException.class)
-          .hasMessage("Unsupported client-api-version: 3. Can only be 1 or 2");
+      Assertions.assertThatIllegalArgumentException()
+          .isThrownBy(() -> newCatalog.initialize("nessie", 
options.buildOrThrow()))
+          .withMessage("Unsupported client-api-version: 3. Can only be 1 or 
2");
     }
   }
+
+  private void commit(String branch, String message, Operation... operations)
+      throws NessieNotFoundException, NessieConflictException {
+    Branch ref = (Branch) api.getReference().refName(branch).get();
+    api.commitMultipleOperations()
+        .branch(ref)
+        .commitMeta(NessieUtil.buildCommitMetadata(message, Map.of()))
+        .operations(Arrays.asList(operations))
+        .commit();
+  }
+
+  private org.projectnessie.model.Namespace fetchNamespace(ContentKey key, 
String branch)
+      throws NessieNotFoundException {
+    Reference reference = api.getReference().refName(branch).get();
+    Content content = 
api.getContent().key(key).reference(reference).get().get(key);
+    return 
content.unwrap(org.projectnessie.model.Namespace.class).orElseThrow();
+  }
+
+  private static TableMetadata newTable() {

Review Comment:
   better to name this `newTableMetadata()` because `newTable()` would rather 
be related to `Table`



##########
nessie/src/test/java/org/apache/iceberg/nessie/TestMultipleClients.java:
##########
@@ -67,33 +71,84 @@ public void afterEach() throws Exception {
   }
 
   @Test
-  public void testListNamespaces() {
+  public void testListNamespaces() throws NessieConflictException, 
NessieNotFoundException {
+    Assertions.assertThat(catalog.listNamespaces()).isEmpty();
+    Assertions.assertThat(anotherCatalog.listNamespaces()).isEmpty();
+
+    // listing a non-existent namespace should return empty
+    
Assertions.assertThat(catalog.listNamespaces(Namespace.of("db1"))).isEmpty();
+    
Assertions.assertThat(anotherCatalog.listNamespaces(Namespace.of("db1"))).isEmpty();
+
     catalog.createNamespace(Namespace.of("db1"), Collections.emptyMap());
+
     
Assertions.assertThat(catalog.listNamespaces()).containsExactlyInAnyOrder(Namespace.of("db1"));
+    Assertions.assertThat(anotherCatalog.listNamespaces())
+        .containsExactlyInAnyOrder(Namespace.of("db1"));
 
     // another client creates a namespace with the same nessie server
     anotherCatalog.createNamespace(Namespace.of("db2"), 
Collections.emptyMap());
-    Assertions.assertThat(anotherCatalog.listNamespaces())
-        .containsExactlyInAnyOrder(Namespace.of("db1"), Namespace.of("db2"));
 
     Assertions.assertThat(catalog.listNamespaces())
         .containsExactlyInAnyOrder(Namespace.of("db1"), Namespace.of("db2"));
+    Assertions.assertThat(anotherCatalog.listNamespaces())
+        .containsExactlyInAnyOrder(Namespace.of("db1"), Namespace.of("db2"));
+
+    api.deleteBranch().branch((Branch) 
api.getReference().refName(branch).get()).delete();
+
+    Assertions.assertThatThrownBy(() -> catalog.listNamespaces())
+        .hasMessageContaining(
+            "Cannot list top-level namespaces: ref '%s' is no longer valid", 
branch);
+    Assertions.assertThatThrownBy(() -> 
anotherCatalog.listNamespaces(Namespace.of("db1")))
+        .hasMessageContaining(
+            "Cannot list child namespaces from 'db1': ref '%s' is no longer 
valid", branch);
   }
 
   @Test
-  public void testLoadNamespaceMetadata() {
+  public void testLoadNamespaceMetadata() throws NessieConflictException, 
NessieNotFoundException {
+

Review Comment:
   unnecessary whitespace



##########
nessie/src/test/java/org/apache/iceberg/nessie/TestNessieIcebergClient.java:
##########
@@ -91,15 +113,444 @@ public void testWithReferenceAfterRecreatingBranch()
     Assertions.assertThat(client.withReference(branch, 
null)).isNotEqualTo(client);
   }
 
+  @Test
+  public void testCreateNamespace() throws NessieConflictException, 
NessieNotFoundException {
+    String branch = "createNamespaceBranch";
+    createBranch(branch);
+    Map<String, String> catalogOptions =
+        Map.of(
+            CatalogProperties.USER, "iceberg-user",
+            CatalogProperties.APP_ID, "iceberg-nessie");
+    NessieIcebergClient client = new NessieIcebergClient(api, branch, null, 
catalogOptions);
+
+    client.createNamespace(Namespace.of("a"), Map.of());
+    
Assertions.assertThat(client.listNamespaces(Namespace.of("a"))).isNotNull();
+
+    List<LogResponse.LogEntry> entries = 
api.getCommitLog().refName(branch).get().getLogEntries();
+    Assertions.assertThat(entries)
+        .isNotEmpty()
+        .first()
+        .extracting(LogResponse.LogEntry::getCommitMeta)
+        .extracting(CommitMeta::getMessage, CommitMeta::getAuthor, 
CommitMeta::getProperties)
+        .containsExactly(
+            "create namespace a",
+            "iceberg-user",
+            ImmutableMap.of(
+                "application-type", "iceberg",
+                "app-id", "iceberg-nessie"));
+  }
+
+  @Test
+  public void testCreateNamespaceInvalid() throws NessieConflictException, 
NessieNotFoundException {
+    String branch = "createNamespaceInvalidBranch";
+    createBranch(branch);
+    NessieIcebergClient client = new NessieIcebergClient(api, branch, null, 
Map.of());
+
+    Assertions.assertThatIllegalArgumentException()
+        .isThrownBy(() -> client.createNamespace(Namespace.empty(), Map.of()))
+        .withMessageContaining("Creating empty namespaces is not supported");
+
+    Assertions.assertThatThrownBy(() -> 
client.createNamespace(Namespace.of("a", "b"), Map.of()))
+        .isInstanceOf(NoSuchNamespaceException.class)
+        .hasMessageContaining("Cannot create namespace 'a.b': parent namespace 
'a' does not exist");
+  }
+
+  @Test
+  public void testCreateNamespaceConflict()
+      throws NessieConflictException, NessieNotFoundException {
+    String branch = "createNamespaceConflictBranch";
+    createBranch(branch);
+    NessieIcebergClient client = new NessieIcebergClient(api, branch, null, 
Map.of());
+
+    client.createNamespace(Namespace.of("a"), Map.of());
+
+    Assertions.assertThatThrownBy(() -> 
client.createNamespace(Namespace.of("a"), Map.of()))
+        .isInstanceOf(AlreadyExistsException.class)
+        .hasMessageContaining("Namespace already exists: a");
+
+    client.commitTable(
+        null, newTable(), "file:///tmp/iceberg", (String) null, 
ContentKey.of("a", "tbl"));
+
+    Assertions.assertThatThrownBy(() -> 
client.createNamespace(Namespace.of("a", "tbl"), Map.of()))
+        .isInstanceOf(AlreadyExistsException.class)
+        .hasMessageContaining("Another content object with name 'a.tbl' 
already exists");
+  }
+
+  @Test
+  public void testCreateNamespaceExternalConflict()
+      throws NessieConflictException, NessieNotFoundException {
+    String branch = "createNamespaceExternalConflictBranch";
+    createBranch(branch);
+    NessieIcebergClient client = new NessieIcebergClient(api, branch, null, 
Map.of());
+
+    org.projectnessie.model.Namespace nessieNs =
+        org.projectnessie.model.Namespace.of(ContentKey.of("a"));
+    commit(branch, "create namespace a", Operation.Put.of(ContentKey.of("a"), 
nessieNs));
+
+    Assertions.assertThatThrownBy(() -> 
client.createNamespace(Namespace.of("a"), Map.of()))
+        .isInstanceOf(AlreadyExistsException.class)
+        .hasMessageContaining("Namespace already exists: a");
+
+    IcebergTable table = IcebergTable.of("file:///tmp/iceberg", 1, 1, 1, 1);
+    commit(branch, "create table a.tbl2", Operation.Put.of(ContentKey.of("a", 
"tbl"), table));
+
+    Assertions.assertThatThrownBy(() -> 
client.createNamespace(Namespace.of("a", "tbl"), Map.of()))
+        .isInstanceOf(AlreadyExistsException.class)
+        .hasMessageContaining("Another content object with name 'a.tbl' 
already exists");
+  }
+
+  @Test
+  public void testCreateNamespaceNonExistingRef()
+      throws NessieConflictException, NessieNotFoundException {
+    String branch = "createNamespaceNonExistingRefBranch";
+    createBranch(branch);
+    NessieIcebergClient client = new NessieIcebergClient(api, branch, null, 
Map.of());
+
+    client.createNamespace(Namespace.of("a"), Map.of());
+
+    api.deleteBranch().branch((Branch) 
api.getReference().refName(branch).get()).delete();
+
+    Assertions.assertThatThrownBy(() -> 
client.createNamespace(Namespace.of("b"), Map.of()))
+        .isInstanceOf(RuntimeException.class)
+        .hasMessageContaining(
+            "Cannot create namespace 'b': ref 
'createNamespaceNonExistingRefBranch' is no longer valid");
+  }
+
+  @Test
+  public void testDropNamespace() throws NessieConflictException, 
NessieNotFoundException {
+    String branch = "dropNamespaceBranch";
+    createBranch(branch);
+    Map<String, String> catalogOptions =
+        Map.of(
+            CatalogProperties.USER, "iceberg-user",
+            CatalogProperties.APP_ID, "iceberg-nessie");
+    NessieIcebergClient client = new NessieIcebergClient(api, branch, null, 
catalogOptions);
+
+    Namespace parent = Namespace.of("a");
+    Namespace child = Namespace.of("a", "b");
+
+    Assertions.assertThat(client.dropNamespace(parent)).isFalse();
+    Assertions.assertThat(client.dropNamespace(child)).isFalse();
+
+    client.createNamespace(parent, Map.of());
+    client.createNamespace(child, Map.of());
+
+    Assertions.assertThat(client.dropNamespace(child)).isTrue();
+    Assertions.assertThat(client.dropNamespace(parent)).isTrue();
+
+    List<LogResponse.LogEntry> entries = 
api.getCommitLog().refName(branch).get().getLogEntries();
+    Assertions.assertThat(entries)
+        .isNotEmpty()
+        .extracting(LogResponse.LogEntry::getCommitMeta)
+        .satisfies(
+            meta -> {
+              Assertions.assertThat(meta.getMessage()).contains("drop 
namespace a");
+              
Assertions.assertThat(meta.getAuthor()).isEqualTo("iceberg-user");
+              Assertions.assertThat(meta.getProperties())
+                  .containsEntry(NessieUtil.APPLICATION_TYPE, "iceberg")
+                  .containsEntry(CatalogProperties.APP_ID, "iceberg-nessie");
+            },
+            Index.atIndex(0))
+        .satisfies(
+            meta -> {
+              Assertions.assertThat(meta.getMessage()).contains("drop 
namespace a.b");
+              
Assertions.assertThat(meta.getAuthor()).isEqualTo("iceberg-user");
+              Assertions.assertThat(meta.getProperties())
+                  .containsEntry(NessieUtil.APPLICATION_TYPE, "iceberg")
+                  .containsEntry(CatalogProperties.APP_ID, "iceberg-nessie");
+            },
+            Index.atIndex(1));
+  }
+
+  @Test
+  public void testDropNamespaceNotEmpty() throws NessieConflictException, 
NessieNotFoundException {
+    String branch = "dropNamespaceInvalidBranch";
+    createBranch(branch);
+    NessieIcebergClient client = new NessieIcebergClient(api, branch, null, 
Map.of());
+
+    client.createNamespace(Namespace.of("a"), Map.of());
+    client.createNamespace(Namespace.of("a", "b"), Map.of());
+
+    Assertions.assertThatThrownBy(() -> 
client.dropNamespace(Namespace.of("a")))
+        .isInstanceOf(NamespaceNotEmptyException.class)
+        .hasMessageContaining("Namespace 'a' is not empty.");
+  }
+
+  @Test
+  public void testDropNamespaceConflict() throws NessieConflictException, 
NessieNotFoundException {
+    String branch = "dropNamespaceConflictBranch";
+    createBranch(branch);
+    NessieIcebergClient client = new NessieIcebergClient(api, branch, null, 
Map.of());
+
+    client.createNamespace(Namespace.of("a"), Map.of());
+
+    client.commitTable(
+        null, newTable(), "file:///tmp/iceberg", (String) null, 
ContentKey.of("a", "tbl"));
+
+    Assertions.assertThatThrownBy(() -> client.dropNamespace(Namespace.of("a", 
"tbl")))
+        .isInstanceOf(NoSuchNamespaceException.class)
+        .hasMessageContaining("Content object with name 'a.tbl' is not a 
namespace.");
+  }
+
+  @Test
+  public void testDropNamespaceExternalConflict()
+      throws NessieConflictException, NessieNotFoundException {
+    String branch = "dropNamespaceExternalConflictBranch";
+    createBranch(branch);
+    NessieIcebergClient client = new NessieIcebergClient(api, branch, null, 
Map.of());
+
+    client.createNamespace(Namespace.of("a"), Map.of());
+
+    org.projectnessie.model.Namespace original = 
fetchNamespace(ContentKey.of("a"), branch);
+    org.projectnessie.model.Namespace updated =
+        org.projectnessie.model.Namespace.builder()
+            .from(original)
+            .properties(Map.of("k1", "v1"))
+            .build();
+    commit(branch, "update namespace a", Operation.Put.of(ContentKey.of("a"), 
updated));
+
+    Assertions.assertThatThrownBy(() -> 
client.dropNamespace(Namespace.of("a")))
+        .isInstanceOf(RuntimeException.class)
+        .hasMessageContaining(
+            "Cannot drop namespace 'a': Values of existing and expected 
content for key 'a' are different.");
+  }
+
+  @Test
+  public void testDropNamespaceNonExistingRef()
+      throws NessieConflictException, NessieNotFoundException {
+    String branch = "dropNamespaceNonExistingRefBranch";
+    createBranch(branch);
+    NessieIcebergClient client = new NessieIcebergClient(api, branch, null, 
Map.of());
+
+    client.createNamespace(Namespace.of("a"), Map.of());
+
+    api.deleteBranch().branch((Branch) 
api.getReference().refName(branch).get()).delete();
+
+    Assertions.assertThat(client.dropNamespace(Namespace.of("a"))).isFalse();
+  }
+
+  @Test
+  public void testSetProperties() throws NessieConflictException, 
NessieNotFoundException {
+    String branch = "setPropertiesBranch";
+    createBranch(branch);
+    Map<String, String> catalogOptions =
+        Map.of(
+            CatalogProperties.USER, "iceberg-user",
+            CatalogProperties.APP_ID, "iceberg-nessie");
+    NessieIcebergClient client = new NessieIcebergClient(api, branch, null, 
catalogOptions);
+
+    Namespace ns = Namespace.of("a");
+    client.createNamespace(ns, Map.of("k1", "v1a"));
+
+    Assertions.assertThat(client.setProperties(ns, Map.of("k1", "v1b", "k2", 
"v2"))).isTrue();
+
+    Assertions.assertThat(client.loadNamespaceMetadata(ns))
+        .hasSize(2)
+        .containsEntry("k1", "v1b")
+        .containsEntry("k2", "v2");
+
+    List<LogResponse.LogEntry> entries = 
api.getCommitLog().refName(branch).get().getLogEntries();
+    Assertions.assertThat(entries)
+        .isNotEmpty()
+        .first()
+        .extracting(LogResponse.LogEntry::getCommitMeta)
+        .satisfies(
+            meta -> {
+              Assertions.assertThat(meta.getMessage()).contains("update 
namespace a");
+              
Assertions.assertThat(meta.getAuthor()).isEqualTo("iceberg-user");
+              Assertions.assertThat(meta.getProperties())
+                  .containsEntry(NessieUtil.APPLICATION_TYPE, "iceberg")
+                  .containsEntry(CatalogProperties.APP_ID, "iceberg-nessie");
+            });
+  }
+
+  @Test
+  public void testSetPropertiesExternalConflict()
+      throws NessieConflictException, NessieNotFoundException {
+    String branch = "setPropertiesExternalConflictBranch";
+    createBranch(branch);
+    NessieIcebergClient client = new NessieIcebergClient(api, branch, null, 
Map.of());
+
+    Namespace ns = Namespace.of("a");
+    client.createNamespace(ns, Map.of("k1", "v1a"));
+
+    ContentKey key = ContentKey.of("a");
+    org.projectnessie.model.Namespace original = fetchNamespace(key, branch);
+    org.projectnessie.model.Namespace updated =
+        org.projectnessie.model.Namespace.builder()
+            .from(original)
+            .properties(Map.of("k1", "v1b", "k2", "v2"))
+            .build();
+    commit(branch, "update namespace a", Operation.Put.of(key, updated));
+
+    // will generate a conflict and a retry
+    Assertions.assertThat(client.setProperties(ns, Map.of("k1", "v1c", "k3", 
"v3"))).isTrue();
+
+    Assertions.assertThat(client.loadNamespaceMetadata(ns))
+        .hasSize(3)
+        .containsEntry("k1", "v1c")
+        .containsEntry("k2", "v2")
+        .containsEntry("k3", "v3");
+  }
+
+  @Test
+  public void testSetPropertiesNonExistingNs()
+      throws NessieConflictException, NessieNotFoundException {
+    String branch = "setPropertiesNonExistingNsBranch";
+    createBranch(branch);
+    NessieIcebergClient client = new NessieIcebergClient(api, branch, null, 
Map.of());
+
+    client.createNamespace(Namespace.of("a"), Map.of());
+
+    commit(branch, "delete namespace a", 
Operation.Delete.of(ContentKey.of("a")));
+
+    Assertions.assertThatThrownBy(
+            () -> client.setProperties(Namespace.of("a"), Map.of("k1", "v1a")))
+        .isInstanceOf(NoSuchNamespaceException.class)
+        .hasMessageContaining("Namespace does not exist: a");
+  }
+
+  @Test
+  public void testSetPropertiesNonExistingRef()
+      throws NessieConflictException, NessieNotFoundException {
+    String branch = "setPropertiesNonExistingRefBranch";
+    createBranch(branch);
+    NessieIcebergClient client = new NessieIcebergClient(api, branch, null, 
Map.of());
+
+    client.createNamespace(Namespace.of("a"), Map.of());
+
+    api.deleteBranch().branch((Branch) 
api.getReference().refName(branch).get()).delete();
+
+    Assertions.assertThatThrownBy(() -> 
client.setProperties(Namespace.of("a"), Map.of("k1", "v1")))
+        .isInstanceOf(RuntimeException.class)
+        .hasMessageContaining(
+            "Cannot update properties on namespace 'a': ref 
'setPropertiesNonExistingRefBranch' is no longer valid");
+  }
+
+  @Test
+  public void testRemoveProperties() throws NessieConflictException, 
NessieNotFoundException {
+    String branch = "removePropertiesBranch";
+    createBranch(branch);
+    Map<String, String> catalogOptions =
+        Map.of(
+            CatalogProperties.USER, "iceberg-user",
+            CatalogProperties.APP_ID, "iceberg-nessie");
+    NessieIcebergClient client = new NessieIcebergClient(api, branch, null, 
catalogOptions);
+
+    Namespace ns = Namespace.of("a");
+
+    client.createNamespace(ns, Map.of("k1", "v1", "k2", "v2"));
+
+    Assertions.assertThat(client.removeProperties(ns, Set.of("k1"))).isTrue();
+
+    
Assertions.assertThat(client.loadNamespaceMetadata(ns)).hasSize(1).containsOnlyKeys("k2");
+
+    List<LogResponse.LogEntry> entries = 
api.getCommitLog().refName(branch).get().getLogEntries();
+    Assertions.assertThat(entries)
+        .isNotEmpty()
+        .first()
+        .extracting(LogResponse.LogEntry::getCommitMeta)
+        .satisfies(
+            meta -> {
+              Assertions.assertThat(meta.getMessage()).contains("update 
namespace a");
+              
Assertions.assertThat(meta.getAuthor()).isEqualTo("iceberg-user");
+              Assertions.assertThat(meta.getProperties())
+                  .containsEntry(NessieUtil.APPLICATION_TYPE, "iceberg")
+                  .containsEntry(CatalogProperties.APP_ID, "iceberg-nessie");
+            });
+  }
+
+  @Test
+  public void testRemovePropertiesExternalConflict()
+      throws NessieConflictException, NessieNotFoundException {
+    String branch = "removePropertiesExternalConflictBranch";
+    createBranch(branch);
+    NessieIcebergClient client = new NessieIcebergClient(api, branch, null, 
Map.of());
+
+    Namespace ns = Namespace.of("a");
+    client.createNamespace(ns, Map.of("k1", "v1"));
+
+    ContentKey key = ContentKey.of("a");
+    org.projectnessie.model.Namespace original = fetchNamespace(key, branch);
+    org.projectnessie.model.Namespace updated =
+        org.projectnessie.model.Namespace.builder()
+            .from(original)
+            .properties(Map.of("k2", "v2", "k3", "v3"))
+            .build();
+    commit(branch, "update namespace a", Operation.Put.of(key, updated));
+
+    // will generate a conflict and a retry
+    Assertions.assertThat(client.removeProperties(ns, Set.of("k2"))).isTrue();
+
+    
Assertions.assertThat(client.loadNamespaceMetadata(ns)).hasSize(1).containsOnlyKeys("k3");
+  }
+
+  @Test
+  public void testRemovePropertiesNonExistingNs()
+      throws NessieConflictException, NessieNotFoundException {
+    String branch = "removePropertiesNonExistingNsBranch";
+    createBranch(branch);
+    NessieIcebergClient client = new NessieIcebergClient(api, branch, null, 
Map.of());
+
+    client.createNamespace(Namespace.of("a"), Map.of("k1", "v1"));
+
+    commit(branch, "delete namespace a", 
Operation.Delete.of(ContentKey.of("a")));
+
+    Assertions.assertThatThrownBy(() -> 
client.removeProperties(Namespace.of("a"), Set.of("k1")))
+        .isInstanceOf(NoSuchNamespaceException.class)
+        .hasMessageContaining("Namespace does not exist: a");
+  }
+
+  @Test
+  public void testRemovePropertiesNonExistingRef()
+      throws NessieConflictException, NessieNotFoundException {
+    String branch = "removePropertiesNonExistingRefBranch";
+    createBranch(branch);
+    NessieIcebergClient client = new NessieIcebergClient(api, branch, null, 
Map.of());
+
+    client.createNamespace(Namespace.of("a"), Map.of("k1", "v1"));
+
+    api.deleteBranch().branch((Branch) 
api.getReference().refName(branch).get()).delete();
+
+    Assertions.assertThatThrownBy(() -> 
client.removeProperties(Namespace.of("a"), Set.of("k1")))
+        .isInstanceOf(RuntimeException.class)
+        .hasMessageContaining(
+            "Cannot update properties on namespace 'a': ref 
'removePropertiesNonExistingRefBranch' is no longer valid");
+  }
+
   @Test
   public void testInvalidClientApiVersion() throws IOException {
     try (NessieCatalog newCatalog = new NessieCatalog()) {
       newCatalog.setConf(hadoopConfig);
       ImmutableMap.Builder<String, String> options =
           ImmutableMap.<String, String>builder().put("client-api-version", 
"3");
-      Assertions.assertThatThrownBy(() -> newCatalog.initialize("nessie", 
options.buildOrThrow()))
-          .isInstanceOf(IllegalArgumentException.class)
-          .hasMessage("Unsupported client-api-version: 3. Can only be 1 or 2");
+      Assertions.assertThatIllegalArgumentException()
+          .isThrownBy(() -> newCatalog.initialize("nessie", 
options.buildOrThrow()))
+          .withMessage("Unsupported client-api-version: 3. Can only be 1 or 
2");
     }
   }
+
+  private void commit(String branch, String message, Operation... operations)
+      throws NessieNotFoundException, NessieConflictException {
+    Branch ref = (Branch) api.getReference().refName(branch).get();
+    api.commitMultipleOperations()
+        .branch(ref)
+        .commitMeta(NessieUtil.buildCommitMetadata(message, Map.of()))
+        .operations(Arrays.asList(operations))
+        .commit();
+  }
+
+  private org.projectnessie.model.Namespace fetchNamespace(ContentKey key, 
String branch)
+      throws NessieNotFoundException {
+    Reference reference = api.getReference().refName(branch).get();
+    Content content = 
api.getContent().key(key).reference(reference).get().get(key);
+    return 
content.unwrap(org.projectnessie.model.Namespace.class).orElseThrow();
+  }
+
+  private static TableMetadata newTable() {
+    Schema schema =
+        new Schema(Types.StructType.of(required(1, "id", 
Types.LongType.get())).fields());

Review Comment:
   I think this can be simplified to `new Schema(required(1, "id", 
Types.LongType.get()));`



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscr...@iceberg.apache.org

For queries about this service, please contact Infrastructure at:
us...@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscr...@iceberg.apache.org
For additional commands, e-mail: issues-h...@iceberg.apache.org


Reply via email to