Wip: Refactor command implementation and enable i18n for CLI

This commit is contained in:
Paul Schaub 2022-06-06 20:06:14 +02:00
parent a7f02d58cc
commit 4934e472e2
Signed by: vanitasvitae
GPG key ID: 62BEE9264BF17311
34 changed files with 980 additions and 782 deletions

View file

@ -9,41 +9,44 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.Date;
import org.junit.jupiter.api.Test;
import sop.cli.picocli.commands.AbstractSopCmd;
import sop.cli.picocli.commands.ArmorCmd;
import sop.util.UTCUtil;
public class DateParserTest {
public class DateParsingTest {
private AbstractSopCmd cmd = new ArmorCmd(); // we use ArmorCmd as a concrete implementation.
@Test
public void parseNotAfterDashReturnsEndOfTime() {
assertEquals(DateParser.END_OF_TIME, DateParser.parseNotAfter("-"));
assertEquals(AbstractSopCmd.END_OF_TIME, cmd.parseNotAfter("-"));
}
@Test
public void parseNotBeforeDashReturnsBeginningOfTime() {
assertEquals(DateParser.BEGINNING_OF_TIME, DateParser.parseNotBefore("-"));
assertEquals(AbstractSopCmd.BEGINNING_OF_TIME, cmd.parseNotBefore("-"));
}
@Test
public void parseNotAfterNowReturnsNow() {
assertEquals(new Date().getTime(), DateParser.parseNotAfter("now").getTime(), 1000);
assertEquals(new Date().getTime(), cmd.parseNotAfter("now").getTime(), 1000);
}
@Test
public void parseNotBeforeNowReturnsNow() {
assertEquals(new Date().getTime(), DateParser.parseNotBefore("now").getTime(), 1000);
assertEquals(new Date().getTime(), cmd.parseNotBefore("now").getTime(), 1000);
}
@Test
public void parseNotAfterTimestamp() {
String timestamp = "2019-10-24T23:48:29Z";
Date date = DateParser.parseNotAfter(timestamp);
Date date = cmd.parseNotAfter(timestamp);
assertEquals(timestamp, UTCUtil.formatUTCDate(date));
}
@Test
public void parseNotBeforeTimestamp() {
String timestamp = "2019-10-29T18:36:45Z";
Date date = DateParser.parseNotBefore(timestamp);
Date date = cmd.parseNotBefore(timestamp);
assertEquals(timestamp, UTCUtil.formatUTCDate(date));
}
}

View file

@ -1,123 +0,0 @@
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
//
// SPDX-License-Identifier: Apache-2.0
package sop.cli.picocli;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.file.Files;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import sop.exception.SOPGPException;
public class FileUtilTest {
@BeforeAll
public static void setup() {
FileUtil.setEnvironmentVariableResolver(new FileUtil.EnvironmentVariableResolver() {
@Override
public String resolveEnvironmentVariable(String name) {
if (name.equals("test123")) {
return "test321";
}
return null;
}
});
}
@Test
public void getFile_ThrowsForNull() {
assertThrows(NullPointerException.class, () -> FileUtil.getFile(null));
}
@Test
public void getFile_prfxEnvAlreadyExists() throws IOException {
File tempFile = new File("@ENV:test");
tempFile.createNewFile();
tempFile.deleteOnExit();
assertThrows(SOPGPException.AmbiguousInput.class, () -> FileUtil.getFile("@ENV:test"));
}
@Test
public void getFile_EnvironmentVariable() {
File file = FileUtil.getFile("@ENV:test123");
assertEquals("test321", file.getName());
}
@Test
public void getFile_nonExistentEnvVariable() {
assertThrows(IllegalArgumentException.class, () -> FileUtil.getFile("@ENV:INVALID"));
}
@Test
public void getFile_prfxFdAlreadyExists() throws IOException {
File tempFile = new File("@FD:1");
tempFile.createNewFile();
tempFile.deleteOnExit();
assertThrows(SOPGPException.AmbiguousInput.class, () -> FileUtil.getFile("@FD:1"));
}
@Test
public void getFile_prfxFdNotSupported() {
assertThrows(IllegalArgumentException.class, () -> FileUtil.getFile("@FD:2"));
}
@Test
public void createNewFileOrThrow_throwsForNull() {
assertThrows(NullPointerException.class, () -> FileUtil.createNewFileOrThrow(null));
}
@Test
public void createNewFileOrThrow_success() throws IOException {
File dir = Files.createTempDirectory("test").toFile();
dir.deleteOnExit();
File file = new File(dir, "file");
assertFalse(file.exists());
FileUtil.createNewFileOrThrow(file);
assertTrue(file.exists());
}
@Test
public void createNewFileOrThrow_alreadyExists() throws IOException {
File dir = Files.createTempDirectory("test").toFile();
dir.deleteOnExit();
File file = new File(dir, "file");
FileUtil.createNewFileOrThrow(file);
assertTrue(file.exists());
assertThrows(SOPGPException.OutputExists.class, () -> FileUtil.createNewFileOrThrow(file));
}
@Test
public void getFileInputStream_success() throws IOException {
File dir = Files.createTempDirectory("test").toFile();
dir.deleteOnExit();
File file = new File(dir, "file");
FileUtil.createNewFileOrThrow(file);
FileInputStream inputStream = FileUtil.getFileInputStream(file.getAbsolutePath());
assertNotNull(inputStream);
}
@Test
public void getFileInputStream_fileNotFound() throws IOException {
File dir = Files.createTempDirectory("test").toFile();
dir.deleteOnExit();
File file = new File(dir, "file");
assertThrows(SOPGPException.MissingInput.class,
() -> FileUtil.getFileInputStream(file.getAbsolutePath()));
}
}

View file

@ -23,8 +23,8 @@ import sop.operation.ExtractCert;
import sop.operation.GenerateKey;
import sop.operation.InlineSign;
import sop.operation.InlineVerify;
import sop.operation.Sign;
import sop.operation.Verify;
import sop.operation.DetachedSign;
import sop.operation.DetachedVerify;
import sop.operation.Version;
public class SOPTest {
@ -65,12 +65,12 @@ public class SOPTest {
}
@Override
public Sign sign() {
public DetachedSign detachedSign() {
return null;
}
@Override
public Verify verify() {
public DetachedVerify detachedVerify() {
return null;
}

View file

@ -36,7 +36,6 @@ import sop.ReadyWithResult;
import sop.SOP;
import sop.SessionKey;
import sop.Verification;
import sop.cli.picocli.DateParser;
import sop.cli.picocli.SopCLI;
import sop.cli.picocli.TestFileUtil;
import sop.exception.SOPGPException;
@ -116,7 +115,7 @@ public class DecryptCmdTest {
public void assertDefaultTimeRangesAreUsedIfNotOverwritten() throws SOPGPException.UnsupportedOption {
Date now = new Date();
SopCLI.main(new String[] {"decrypt"});
verify(decrypt, times(1)).verifyNotBefore(DateParser.BEGINNING_OF_TIME);
verify(decrypt, times(1)).verifyNotBefore(AbstractSopCmd.BEGINNING_OF_TIME);
verify(decrypt, times(1)).verifyNotAfter(
ArgumentMatchers.argThat(argument -> {
// allow 1-second difference
@ -127,8 +126,8 @@ public class DecryptCmdTest {
@Test
public void assertVerifyNotAfterAndBeforeDashResultsInMaxTimeRange() throws SOPGPException.UnsupportedOption {
SopCLI.main(new String[] {"decrypt", "--not-before", "-", "--not-after", "-"});
verify(decrypt, times(1)).verifyNotBefore(DateParser.BEGINNING_OF_TIME);
verify(decrypt, times(1)).verifyNotAfter(DateParser.END_OF_TIME);
verify(decrypt, times(1)).verifyNotBefore(AbstractSopCmd.BEGINNING_OF_TIME);
verify(decrypt, times(1)).verifyNotAfter(AbstractSopCmd.END_OF_TIME);
}
@Test

View file

@ -127,7 +127,7 @@ public class EncryptCmdTest {
@Test
@ExpectSystemExitWithStatus(79)
public void signWith_certCannotSignCausesExit1() throws IOException, SOPGPException.KeyIsProtected, SOPGPException.UnsupportedAsymmetricAlgo, SOPGPException.KeyCannotSign, SOPGPException.BadData {
public void signWith_certCannotSignCausesExit79() throws IOException, SOPGPException.KeyIsProtected, SOPGPException.UnsupportedAsymmetricAlgo, SOPGPException.KeyCannotSign, SOPGPException.BadData {
when(encrypt.signWith((InputStream) any())).thenThrow(new SOPGPException.KeyCannotSign());
File keyFile = File.createTempFile("sign-with", ".asc");
File passwordFile = TestFileUtil.writeTempStringFile("dragon");

View file

@ -24,17 +24,17 @@ import sop.SOP;
import sop.SigningResult;
import sop.cli.picocli.SopCLI;
import sop.exception.SOPGPException;
import sop.operation.Sign;
import sop.operation.DetachedSign;
public class SignCmdTest {
Sign sign;
DetachedSign detachedSign;
File keyFile;
@BeforeEach
public void mockComponents() throws IOException, SOPGPException.ExpectedText {
sign = mock(Sign.class);
when(sign.data((InputStream) any())).thenReturn(new ReadyWithResult<SigningResult>() {
detachedSign = mock(DetachedSign.class);
when(detachedSign.data((InputStream) any())).thenReturn(new ReadyWithResult<SigningResult>() {
@Override
public SigningResult writeTo(OutputStream outputStream) {
return SigningResult.builder().build();
@ -42,7 +42,7 @@ public class SignCmdTest {
});
SOP sop = mock(SOP.class);
when(sop.sign()).thenReturn(sign);
when(sop.detachedSign()).thenReturn(detachedSign);
SopCLI.setSopInstance(sop);
@ -65,27 +65,27 @@ public class SignCmdTest {
@Test
@ExpectSystemExitWithStatus(37)
public void as_unsupportedOptionCausesExit37() throws SOPGPException.UnsupportedOption {
when(sign.mode(any())).thenThrow(new SOPGPException.UnsupportedOption("Setting signing mode not supported."));
when(detachedSign.mode(any())).thenThrow(new SOPGPException.UnsupportedOption("Setting signing mode not supported."));
SopCLI.main(new String[] {"sign", "--as", "binary", keyFile.getAbsolutePath()});
}
@Test
@ExpectSystemExitWithStatus(1)
public void key_nonExistentKeyFileCausesExit1() {
@ExpectSystemExitWithStatus(61)
public void key_nonExistentKeyFileCausesExit61() {
SopCLI.main(new String[] {"sign", "invalid.asc"});
}
@Test
@ExpectSystemExitWithStatus(1)
public void key_keyIsProtectedCausesExit1() throws SOPGPException.KeyIsProtected, IOException, SOPGPException.BadData {
when(sign.key((InputStream) any())).thenThrow(new SOPGPException.KeyIsProtected());
@ExpectSystemExitWithStatus(67)
public void key_keyIsProtectedCausesExit67() throws SOPGPException.KeyIsProtected, IOException, SOPGPException.BadData {
when(detachedSign.key((InputStream) any())).thenThrow(new SOPGPException.KeyIsProtected());
SopCLI.main(new String[] {"sign", keyFile.getAbsolutePath()});
}
@Test
@ExpectSystemExitWithStatus(41)
public void key_badDataCausesExit41() throws SOPGPException.KeyIsProtected, IOException, SOPGPException.BadData {
when(sign.key((InputStream) any())).thenThrow(new SOPGPException.BadData(new IOException()));
when(detachedSign.key((InputStream) any())).thenThrow(new SOPGPException.BadData(new IOException()));
SopCLI.main(new String[] {"sign", keyFile.getAbsolutePath()});
}
@ -98,19 +98,19 @@ public class SignCmdTest {
@Test
public void noArmor_notCalledByDefault() {
SopCLI.main(new String[] {"sign", keyFile.getAbsolutePath()});
verify(sign, never()).noArmor();
verify(detachedSign, never()).noArmor();
}
@Test
public void noArmor_passedDown() {
SopCLI.main(new String[] {"sign", "--no-armor", keyFile.getAbsolutePath()});
verify(sign, times(1)).noArmor();
verify(detachedSign, times(1)).noArmor();
}
@Test
@ExpectSystemExitWithStatus(1)
public void data_ioExceptionCausesExit1() throws IOException, SOPGPException.ExpectedText {
when(sign.data((InputStream) any())).thenReturn(new ReadyWithResult<SigningResult>() {
when(detachedSign.data((InputStream) any())).thenReturn(new ReadyWithResult<SigningResult>() {
@Override
public SigningResult writeTo(OutputStream outputStream) throws IOException {
throw new IOException();
@ -122,7 +122,7 @@ public class SignCmdTest {
@Test
@ExpectSystemExitWithStatus(53)
public void data_expectedTextExceptionCausesExit53() throws IOException, SOPGPException.ExpectedText {
when(sign.data((InputStream) any())).thenThrow(new SOPGPException.ExpectedText());
when(detachedSign.data((InputStream) any())).thenThrow(new SOPGPException.ExpectedText());
SopCLI.main(new String[] {"sign", keyFile.getAbsolutePath()});
}
}

View file

@ -27,15 +27,14 @@ import org.junit.jupiter.api.Test;
import org.mockito.ArgumentMatchers;
import sop.SOP;
import sop.Verification;
import sop.cli.picocli.DateParser;
import sop.cli.picocli.SopCLI;
import sop.exception.SOPGPException;
import sop.operation.Verify;
import sop.operation.DetachedVerify;
import sop.util.UTCUtil;
public class VerifyCmdTest {
Verify verify;
DetachedVerify detachedVerify;
File signature;
File cert;
@ -45,12 +44,12 @@ public class VerifyCmdTest {
public void prepare() throws SOPGPException.UnsupportedOption, SOPGPException.BadData, SOPGPException.NoSignature, IOException {
originalSout = System.out;
verify = mock(Verify.class);
when(verify.notBefore(any())).thenReturn(verify);
when(verify.notAfter(any())).thenReturn(verify);
when(verify.cert((InputStream) any())).thenReturn(verify);
when(verify.signatures((InputStream) any())).thenReturn(verify);
when(verify.data((InputStream) any())).thenReturn(
detachedVerify = mock(DetachedVerify.class);
when(detachedVerify.notBefore(any())).thenReturn(detachedVerify);
when(detachedVerify.notAfter(any())).thenReturn(detachedVerify);
when(detachedVerify.cert((InputStream) any())).thenReturn(detachedVerify);
when(detachedVerify.signatures((InputStream) any())).thenReturn(detachedVerify);
when(detachedVerify.data((InputStream) any())).thenReturn(
Collections.singletonList(
new Verification(
UTCUtil.parseUTCDate("2019-10-29T18:36:45Z"),
@ -60,7 +59,7 @@ public class VerifyCmdTest {
);
SOP sop = mock(SOP.class);
when(sop.verify()).thenReturn(verify);
when(sop.detachedVerify()).thenReturn(detachedVerify);
SopCLI.setSopInstance(sop);
@ -77,26 +76,26 @@ public class VerifyCmdTest {
public void notAfter_passedDown() throws SOPGPException.UnsupportedOption {
Date date = UTCUtil.parseUTCDate("2019-10-29T18:36:45Z");
SopCLI.main(new String[] {"verify", "--not-after", "2019-10-29T18:36:45Z", signature.getAbsolutePath(), cert.getAbsolutePath()});
verify(verify, times(1)).notAfter(date);
verify(detachedVerify, times(1)).notAfter(date);
}
@Test
public void notAfter_now() throws SOPGPException.UnsupportedOption {
Date now = new Date();
SopCLI.main(new String[] {"verify", "--not-after", "now", signature.getAbsolutePath(), cert.getAbsolutePath()});
verify(verify, times(1)).notAfter(dateMatcher(now));
verify(detachedVerify, times(1)).notAfter(dateMatcher(now));
}
@Test
public void notAfter_dashCountsAsEndOfTime() throws SOPGPException.UnsupportedOption {
SopCLI.main(new String[] {"verify", "--not-after", "-", signature.getAbsolutePath(), cert.getAbsolutePath()});
verify(verify, times(1)).notAfter(DateParser.END_OF_TIME);
verify(detachedVerify, times(1)).notAfter(AbstractSopCmd.END_OF_TIME);
}
@Test
@ExpectSystemExitWithStatus(37)
public void notAfter_unsupportedOptionCausesExit37() throws SOPGPException.UnsupportedOption {
when(verify.notAfter(any())).thenThrow(new SOPGPException.UnsupportedOption("Setting upper signature date boundary not supported."));
when(detachedVerify.notAfter(any())).thenThrow(new SOPGPException.UnsupportedOption("Setting upper signature date boundary not supported."));
SopCLI.main(new String[] {"verify", "--not-after", "2019-10-29T18:36:45Z", signature.getAbsolutePath(), cert.getAbsolutePath()});
}
@ -104,34 +103,34 @@ public class VerifyCmdTest {
public void notBefore_passedDown() throws SOPGPException.UnsupportedOption {
Date date = UTCUtil.parseUTCDate("2019-10-29T18:36:45Z");
SopCLI.main(new String[] {"verify", "--not-before", "2019-10-29T18:36:45Z", signature.getAbsolutePath(), cert.getAbsolutePath()});
verify(verify, times(1)).notBefore(date);
verify(detachedVerify, times(1)).notBefore(date);
}
@Test
public void notBefore_now() throws SOPGPException.UnsupportedOption {
Date now = new Date();
SopCLI.main(new String[] {"verify", "--not-before", "now", signature.getAbsolutePath(), cert.getAbsolutePath()});
verify(verify, times(1)).notBefore(dateMatcher(now));
verify(detachedVerify, times(1)).notBefore(dateMatcher(now));
}
@Test
public void notBefore_dashCountsAsBeginningOfTime() throws SOPGPException.UnsupportedOption {
SopCLI.main(new String[] {"verify", "--not-before", "-", signature.getAbsolutePath(), cert.getAbsolutePath()});
verify(verify, times(1)).notBefore(DateParser.BEGINNING_OF_TIME);
verify(detachedVerify, times(1)).notBefore(AbstractSopCmd.BEGINNING_OF_TIME);
}
@Test
@ExpectSystemExitWithStatus(37)
public void notBefore_unsupportedOptionCausesExit37() throws SOPGPException.UnsupportedOption {
when(verify.notBefore(any())).thenThrow(new SOPGPException.UnsupportedOption("Setting lower signature date boundary not supported."));
when(detachedVerify.notBefore(any())).thenThrow(new SOPGPException.UnsupportedOption("Setting lower signature date boundary not supported."));
SopCLI.main(new String[] {"verify", "--not-before", "2019-10-29T18:36:45Z", signature.getAbsolutePath(), cert.getAbsolutePath()});
}
@Test
public void notBeforeAndNotAfterAreCalledWithDefaultValues() throws SOPGPException.UnsupportedOption {
SopCLI.main(new String[] {"verify", signature.getAbsolutePath(), cert.getAbsolutePath()});
verify(verify, times(1)).notAfter(dateMatcher(new Date()));
verify(verify, times(1)).notBefore(DateParser.BEGINNING_OF_TIME);
verify(detachedVerify, times(1)).notAfter(dateMatcher(new Date()));
verify(detachedVerify, times(1)).notBefore(AbstractSopCmd.BEGINNING_OF_TIME);
}
private static Date dateMatcher(Date date) {
@ -139,48 +138,48 @@ public class VerifyCmdTest {
}
@Test
@ExpectSystemExitWithStatus(1)
public void cert_fileNotFoundCausesExit1() {
@ExpectSystemExitWithStatus(61)
public void cert_fileNotFoundCausesExit61() {
SopCLI.main(new String[] {"verify", signature.getAbsolutePath(), "invalid.asc"});
}
@Test
@ExpectSystemExitWithStatus(41)
public void cert_badDataCausesExit41() throws SOPGPException.BadData {
when(verify.cert((InputStream) any())).thenThrow(new SOPGPException.BadData(new IOException()));
when(detachedVerify.cert((InputStream) any())).thenThrow(new SOPGPException.BadData(new IOException()));
SopCLI.main(new String[] {"verify", signature.getAbsolutePath(), cert.getAbsolutePath()});
}
@Test
@ExpectSystemExitWithStatus(1)
public void signature_fileNotFoundCausesExit1() {
@ExpectSystemExitWithStatus(61)
public void signature_fileNotFoundCausesExit61() {
SopCLI.main(new String[] {"verify", "invalid.sig", cert.getAbsolutePath()});
}
@Test
@ExpectSystemExitWithStatus(41)
public void signature_badDataCausesExit41() throws SOPGPException.BadData {
when(verify.signatures((InputStream) any())).thenThrow(new SOPGPException.BadData(new IOException()));
when(detachedVerify.signatures((InputStream) any())).thenThrow(new SOPGPException.BadData(new IOException()));
SopCLI.main(new String[] {"verify", signature.getAbsolutePath(), cert.getAbsolutePath()});
}
@Test
@ExpectSystemExitWithStatus(3)
public void data_noSignaturesCausesExit3() throws SOPGPException.NoSignature, IOException, SOPGPException.BadData {
when(verify.data((InputStream) any())).thenThrow(new SOPGPException.NoSignature());
when(detachedVerify.data((InputStream) any())).thenThrow(new SOPGPException.NoSignature());
SopCLI.main(new String[] {"verify", signature.getAbsolutePath(), cert.getAbsolutePath()});
}
@Test
@ExpectSystemExitWithStatus(41)
public void data_badDataCausesExit41() throws SOPGPException.NoSignature, IOException, SOPGPException.BadData {
when(verify.data((InputStream) any())).thenThrow(new SOPGPException.BadData(new IOException()));
when(detachedVerify.data((InputStream) any())).thenThrow(new SOPGPException.BadData(new IOException()));
SopCLI.main(new String[] {"verify", signature.getAbsolutePath(), cert.getAbsolutePath()});
}
@Test
public void resultIsPrintedProperly() throws SOPGPException.NoSignature, IOException, SOPGPException.BadData {
when(verify.data((InputStream) any())).thenReturn(Arrays.asList(
when(detachedVerify.data((InputStream) any())).thenReturn(Arrays.asList(
new Verification(UTCUtil.parseUTCDate("2019-10-29T18:36:45Z"),
"EB85BB5FA33A75E15E944E63F231550C4F47E38E",
"EB85BB5FA33A75E15E944E63F231550C4F47E38E"),