Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions client/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -124,9 +124,9 @@ task LedgerValidation(type: CreateStartScripts) {
classpath = jar.outputs.files + project.configurations.runtimeClasspath
}

task MultiLedgersValidation(type: CreateStartScripts) {
mainClass = 'com.scalar.dl.client.tool.MultiLedgersValidation'
applicationName = 'validate-ledgers'
task NamespaceCreation(type: CreateStartScripts) {
mainClass = 'com.scalar.dl.client.tool.NamespaceCreation'
applicationName = 'create-namespace'
outputDir = new File(project.buildDir, 'tmp')
classpath = jar.outputs.files + project.configurations.runtimeClasspath
}
Expand Down Expand Up @@ -169,7 +169,7 @@ applicationDistribution.into('bin') {
from(ContractsListing)
from(ContractExecution)
from(LedgerValidation)
from(MultiLedgersValidation)
from(NamespaceCreation)
from(ScalarDl)
from(ScalarDlGc)
from(StateUpdaterSimpleBench)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.scalar.dl.client.tool;

import com.scalar.dl.client.exception.ClientException;
import com.scalar.dl.client.service.ClientService;
import picocli.CommandLine;
import picocli.CommandLine.Command;

@Command(name = "create-namespace", description = "Create a namespace.")
public class NamespaceCreation extends AbstractClientCommand {

@CommandLine.Option(
names = {"--namespace"},
required = true,
paramLabel = "NAMESPACE",
description = "A namespace name to create.")
private String namespace;

public static void main(String[] args) {
int exitCode = new CommandLine(new NamespaceCreation()).execute(args);
System.exit(exitCode);
}

@Override
protected Integer execute(ClientService service) throws ClientException {
service.createNamespace(namespace);
Common.printOutput(null);
return 0;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
HelpCommand.class,
LedgerValidation.class,
SecretRegistration.class,
NamespaceCreation.class,
},
description = {"These are ScalarDL commands used in various situations:"})
public class ScalarDlCommandLine {
Expand Down Expand Up @@ -77,6 +78,8 @@ static void setupSections(CommandLine cmd) {
sections.put(
"%nexecute and list the registered business logic%n",
Arrays.asList(ContractExecution.class, ContractsListing.class));
// Section: manage namespaces.
sections.put("%nmanage namespaces%n", Collections.singletonList(NamespaceCreation.class));
// Section: validate ledger.
sections.put("%nvalidate ledger%n", Collections.singletonList(LedgerValidation.class));
// Section: generic contracts.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
package com.scalar.dl.client.tool;

import static com.scalar.dl.client.tool.CommandLineTestUtils.createDefaultClientPropertiesFile;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import com.scalar.dl.client.config.ClientConfig;
import com.scalar.dl.client.config.GatewayClientConfig;
import com.scalar.dl.client.exception.ClientException;
import com.scalar.dl.client.service.ClientService;
import com.scalar.dl.client.service.ClientServiceFactory;
import com.scalar.dl.ledger.service.StatusCode;
import java.io.File;
import java.nio.file.Path;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import picocli.CommandLine;

public class NamespaceCreationTest {
private CommandLine commandLine;

@BeforeEach
void setup() {
commandLine = new CommandLine(new NamespaceCreation());
}

@Nested
@DisplayName("#call()")
class call {
@Test
@DisplayName("returns 0 as exit code")
void returns0AsExitCode() throws ClientException {
// Arrange
String[] args =
new String[] {
// Set the required options.
"--properties=PROPERTIES_FILE", "--namespace=test_namespace",
};
NamespaceCreation command = parseArgs(args);
ClientService serviceMock = mock(ClientService.class);

// Act
int exitCode = command.execute(serviceMock);

// Assert
assertThat(exitCode).isEqualTo(0);
verify(serviceMock).createNamespace("test_namespace");
}

@Nested
@DisplayName("where useGateway option is true")
class whereUseGatewayOptionIsTrue {
@Test
@DisplayName("create ClientService with GatewayClientConfig")
public void createClientServiceWithGatewayClientConfig(@TempDir Path tempDir)
throws Exception {
// Arrange
File file = createDefaultClientPropertiesFile(tempDir, "client.props");
String propertiesOption = String.format("--properties=%s", file.getAbsolutePath());
String[] args =
new String[] {
// Set the required options.
propertiesOption,
"--namespace=test-namespace",
// Enable Gateway.
"--use-gateway"
};
NamespaceCreation command = parseArgs(args);
ClientServiceFactory factory = mock(ClientServiceFactory.class);
doReturn(mock(ClientService.class)).when(factory).create(any(GatewayClientConfig.class));

// Act
command.call(factory);

// Verify
verify(factory).create(any(GatewayClientConfig.class));
verify(factory, never()).create(any(ClientConfig.class));
}
}

@Nested
@DisplayName("where useGateway option is false")
class whereUseGatewayOptionIsFalse {
@Test
@DisplayName("create ClientService with ClientConfig")
public void createClientServiceWithClientConfig(@TempDir Path tempDir) throws Exception {
// Arrange
File file = createDefaultClientPropertiesFile(tempDir, "client.props");
String propertiesOption = String.format("--properties=%s", file.getAbsolutePath());
String[] args =
new String[] {
// Set the required options.
propertiesOption, "--namespace=test-namespace",
// Gateway is disabled by default.
};
NamespaceCreation command = parseArgs(args);
ClientServiceFactory factory = mock(ClientServiceFactory.class);
doReturn(mock(ClientService.class)).when(factory).create(any(ClientConfig.class));

// Act
command.call(factory);

// Verify
verify(factory).create(any(ClientConfig.class));
verify(factory, never()).create(any(GatewayClientConfig.class));
}
}

@Nested
@DisplayName("where ClientService throws ClientException")
class whereClientExceptionIsThrownByClientService {
@Test
@DisplayName("returns 1 as exit code")
void returns1AsExitCode(@TempDir Path tempDir) throws Exception {
// Arrange
File file = createDefaultClientPropertiesFile(tempDir, "client.props");
String[] args =
new String[] {
// Set the required options.
"--properties=" + file.getAbsolutePath(), "--namespace=test-namespace",
};
NamespaceCreation command = parseArgs(args);
// Mock service that throws an exception.
ClientServiceFactory factoryMock = mock(ClientServiceFactory.class);
ClientService serviceMock = mock(ClientService.class);
when(factoryMock.create(any(ClientConfig.class))).thenReturn(serviceMock);
doThrow(new ClientException("", StatusCode.RUNTIME_ERROR))
.when(serviceMock)
.createNamespace("test-namespace");

// Act
int exitCode = command.call(factoryMock);

// Assert
assertThat(exitCode).isEqualTo(1);
verify(factoryMock).close();
}
}
}

private NamespaceCreation parseArgs(String[] args) {
return CommandLineTestUtils.parseArgs(commandLine, NamespaceCreation.class, args);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ void displaysGroupedSubcommands() {
" execute-contract Execute a specified contract.",
" list-contracts List registered contracts.",
"",
"manage namespaces",
" create-namespace Create a namespace.",
"",
"validate ledger",
" validate-ledger Validate a specified asset in a ledger.",
"",
Expand Down Expand Up @@ -97,6 +100,7 @@ void memberValuesAreProperlySet() {
CommandLine.HelpCommand.class,
LedgerValidation.class,
SecretRegistration.class,
NamespaceCreation.class,
});
}
}
Expand Down