Slackbot
12/24/2023, 11:37 AMAlexander Preuß
12/24/2023, 11:43 AMPulsarAdmin so you will have to mock the return value of PulsarAdmin.tenants() as well, just as you did for the clusters 😉Umut Bilal Okur
12/24/2023, 11:56 AMUmut Bilal Okur
12/24/2023, 11:56 AMUmut Bilal Okur
12/24/2023, 11:56 AMadmin.tenants().createTenant("tenant1", TenantInfoImpl.builder()
.adminRoles(Set.of("role1","role2"))
.allowedClusters(Set.of(clusterName))
.build());
I wonder why this lines doesn't create a tenantChris Bono
12/24/2023, 4:13 PMadmin.tenants() will return null until it is mocked to do otherwise. By default all mock methods that return a non-primitive value will return null .
I believe in your case, if you just delete the
admin.tenants().createTenant("tenant1", TenantInfoImpl.builder()
.adminRoles(Set.of("role1","role2"))
.allowedClusters(Set.of(clusterName))
.build());
admin.tenants().createTenant("tenan2", TenantInfoImpl.builder()
.adminRoles(Set.of("role1","role2"))
.allowedClusters(Set.of(clusterName))
.build());
everything else should work.
You are spinning through the string list of tenants and mocking out the topics/namespaces for each tenant.Umut Bilal Okur
12/24/2023, 4:17 PMUmut Bilal Okur
12/24/2023, 4:17 PMChris Bono
12/24/2023, 10:52 PM@Test
public void testDeleteClusterWithoutAllOption() throws PulsarAdminException {
// Enable chaining mock calls (eg. admin.tenants().getTenants())
pulsarAdmin = mock(PulsarAdmin.class, RETURNS_DEEP_STUBS);
clusters = mock(Clusters.class);
when(pulsarAdmin.clusters()).thenReturn(clusters);
cmdClusters = spy(new CmdClusters(() -> pulsarAdmin));
String clusterName = "my-cluster";
String otherClusterName = "other-cluster";
List<String> tenants = Arrays.asList("tenant1", "tenant2");
when(pulsarAdmin.tenants().getTenants()).thenReturn(tenants);
when(pulsarAdmin.tenants().getTenantInfo("tenant1")).thenReturn(TenantInfoImpl.builder()
.adminRoles(Set.of("role1"))
.allowedClusters(new HashSet<>(Set.of(clusterName, otherClusterName)))
.build());
when(pulsarAdmin.tenants().getTenantInfo("tenant2")).thenReturn(TenantInfoImpl.builder()
.adminRoles(Set.of("role1"))
.allowedClusters(new HashSet<>(Set.of(clusterName, otherClusterName)))
.build());
// Invoke the delete command
cmdClusters.run(new String[]{"delete", clusterName});
// Verify the cluster was deleted
verify(clusters).deleteCluster(clusterName);
// Verify the cluster was removed from each tenants' set of allowedClusters
TenantInfo modifiedTenant1 = TenantInfoImpl.builder()
.adminRoles(Set.of("role1"))
.allowedClusters(Set.of(otherClusterName))
.build();
verify(pulsarAdmin.tenants()).updateTenant(eq("tenant1"), eq(modifiedTenant1));
TenantInfo modifiedTenant2 = TenantInfoImpl.builder()
.adminRoles(Set.of("role1"))
.allowedClusters(Set.of(otherClusterName))
.build();
verify(pulsarAdmin.tenants()).updateTenant(eq("tenant2"), eq(modifiedTenant2));
}
I can tell you what I don’t like about this test though is that it is highly highly mocked out and tailored to the CmdClusters delete implementation for the “else” branch, namely this
Set<String> clusters = getAdmin().tenants().getTenantInfo(tenant).getAllowedClusters();
clusters.remove(cluster);
getAdmin().tenants().updateTenant(tenant, TenantInfo.builder()
.adminRoles(getAdmin().tenants().getTenantInfo(tenant).getAdminRoles())
.allowedClusters(clusters)
.build());
getAdmin().clusters().deleteCluster(cluster);
Other than doing this via an integration test, I am not sure of a better way w/o introducing some middle abstraction (aka DeleteService) which is out of the scope for the issue you are fixing. As you notice, there is not already a test for delete cluster in TestCmdClusters for this exact reason. I would go ahead and add something like the test above though so it at least verifies the cluster is removed from the TenantInfo.clusters set.
You could add another test (something like testDeleteClusterWithAllOption) and that would require you to mock out the implementation for the “if delete all” path in the CmdClusters ⬇️
for (String namespace : getAdmin().namespaces().getNamespaces(tenant)) {
// Partitioned topic's schema must be deleted by deletePartitionedTopic()
// but not delete() for each partition
for (String topic : getAdmin().topics().getPartitionedTopicList(namespace)) {
getAdmin().topics().deletePartitionedTopic(topic, true, true);
}
for (String topic : getAdmin().topics().getList(namespace)) {
getAdmin().topics().delete(topic, true, true);
}
getAdmin().namespaces().deleteNamespace(namespace, true);
}
getAdmin().tenants().deleteTenant(tenant);
which is definiltey more involved and will get ugly quickly.Chris Bono
12/24/2023, 10:55 PMTestCmdClusters unit tests should just be verifying that if a cli is invoked w/ “foo” then the “FooCommand” does in fact get invoked but the inner workings of what the “FooCommand” does should be tested in a “FooCommandTests” test. Here we have multiple things tested in a single place and it gets messy.Chris Bono
12/24/2023, 10:57 PMUmut Bilal Okur
12/25/2023, 8:19 AM