diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f7a95f5..96fb4d3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -33,8 +33,15 @@ jobs: java-version: ${{ matrix.java }} cache: 'maven' - - name: Build with Maven - run: ./mvnw -B package + - name: Build and test with Maven + run: ./mvnw -B verify + + - name: Upload coverage report + if: matrix.os == 'ubuntu-latest' && matrix.java == 21 + uses: actions/upload-artifact@v4 + with: + name: jacoco-report + path: target/site/jacoco - name: Start Tool run: java -jar target/JavaCrypt.jar diff --git a/pom.xml b/pom.xml index f0883b0..01f8712 100644 --- a/pom.xml +++ b/pom.xml @@ -15,9 +15,42 @@ 11 + + + org.junit.jupiter + junit-jupiter + 5.14.4 + test + + + JavaCrypt + + org.apache.maven.plugins + maven-surefire-plugin + 3.6.0 + + + org.jacoco + jacoco-maven-plugin + 0.8.15 + + + + prepare-agent + + + + report + test + + report + + + + org.apache.maven.plugins maven-jar-plugin diff --git a/src/main/java/javacrypt/RunEncrypt.java b/src/main/java/javacrypt/RunEncrypt.java index 413e552..dcfc851 100644 --- a/src/main/java/javacrypt/RunEncrypt.java +++ b/src/main/java/javacrypt/RunEncrypt.java @@ -17,7 +17,6 @@ import java.util.List; import javax.crypto.Cipher; import javax.crypto.NoSuchPaddingException; -import java.nio.file.Files; /** * This class represents the RunEncrypt command, which performs RSA encryption using the public key. @@ -142,26 +141,4 @@ public void run(List args) { System.out.println("Number of bytes transferred: " + size); } - - /** - * Encrypts the input file using the public key and saves the result to the output file. - * - * @param publicKey The public key. - * @param inputFile The input file. - * @param outputFile The output file. - * @return The number of bytes transferred. - * @throws Exception If an error occurs during encryption. - */ - private int encryptDecryptFile(PublicKey publicKey, File inputFile, File outputFile) throws Exception { - try { - Cipher cipher = getCipher(publicKey); - byte[] inputBytes = Files.readAllBytes(inputFile.toPath()); - byte[] encryptedBytes = crypt(inputBytes, publicKey, cipher); - Files.write(outputFile.toPath(), encryptedBytes); - return encryptedBytes.length; - } catch (Exception e) { - System.err.println("Exception: " + e.getMessage()); - throw e; - } - } } diff --git a/src/main/java/javacrypt/TestJava.java b/src/main/java/javacrypt/TestJava.java deleted file mode 100644 index 016a9bd..0000000 --- a/src/main/java/javacrypt/TestJava.java +++ /dev/null @@ -1,8 +0,0 @@ -package javacrypt; - -// Test Class -public class TestJava { - public static void main(String[] args) { - System.out.println("Java compilation and execution successful!"); - } -} diff --git a/src/test/java/javacrypt/CopyTest.java b/src/test/java/javacrypt/CopyTest.java new file mode 100644 index 0000000..c5197f8 --- /dev/null +++ b/src/test/java/javacrypt/CopyTest.java @@ -0,0 +1,29 @@ +package javacrypt; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Random; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +class CopyTest { + + @TempDir + Path dir; + + @Test + void copiesFileLargerThanOneBuffer() throws Exception { + byte[] data = new byte[RunCopy.RSA_COPY_BUFSIZE * 2 + 17]; + new Random(1).nextBytes(data); + Path in = dir.resolve("in.dat"); + Path out = dir.resolve("out.dat"); + Files.write(in, data); + + new RunCopy().run(Arrays.asList(in.toString(), out.toString())); + + assertArrayEquals(data, Files.readAllBytes(out)); + } +} diff --git a/src/test/java/javacrypt/FactoryTest.java b/src/test/java/javacrypt/FactoryTest.java new file mode 100644 index 0000000..c5eb021 --- /dev/null +++ b/src/test/java/javacrypt/FactoryTest.java @@ -0,0 +1,41 @@ +package javacrypt; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +class FactoryTest { + + private final RunnableKeyValueFactory factory = new RunnableKeyValueFactory(new String[][]{ + {"-encrypt", "javacrypt.RunEncrypt"}, + {"-copy", "javacrypt.RunCopy"} + }); + + @Test + void knowsRegisteredKeywords() { + assertTrue(factory.containsKey("-encrypt")); + assertFalse(factory.containsKey("-unknown")); + assertFalse(factory.containsKey(null)); + } + + @Test + void resolvesClassName() { + assertEquals("javacrypt.RunCopy", factory.getClassNameOfKey("-copy")); + assertNull(factory.getClassNameOfKey("-unknown")); + } + + @Test + void instantiatesRunnable() throws Exception { + assertInstanceOf(RunEncrypt.class, factory.getInstanceFromKey("-encrypt")); + assertNull(factory.getInstanceFromKey("-unknown")); + } + + @Test + void acceptsNullMapping() { + assertFalse(new RunnableKeyValueFactory(null).containsKey("-copy")); + } +} diff --git a/src/test/java/javacrypt/RoundtripTest.java b/src/test/java/javacrypt/RoundtripTest.java new file mode 100644 index 0000000..ddd225e --- /dev/null +++ b/src/test/java/javacrypt/RoundtripTest.java @@ -0,0 +1,77 @@ +package javacrypt; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Random; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +class RoundtripTest { + + @TempDir + Path dir; + + private Path priv; + private Path pub; + + @BeforeEach + void generateKeys() { + priv = dir.resolve("priv.key"); + pub = dir.resolve("pub.key"); + new RunGenKeys().run(Arrays.asList(priv.toString(), pub.toString())); + } + + private byte[] roundtrip(byte[] plain) throws Exception { + Path in = dir.resolve("in.dat"); + Path enc = dir.resolve("enc.dat"); + Path dec = dir.resolve("dec.dat"); + Files.write(in, plain); + + new RunEncrypt().run(Arrays.asList(pub.toString(), in.toString(), enc.toString())); + new RunDecrypt().run(Arrays.asList(priv.toString(), enc.toString(), dec.toString())); + + return Files.readAllBytes(dec); + } + + @Test + void genKeysWritesBothKeyFiles() throws Exception { + assertTrue(Files.size(priv) > 0); + assertTrue(Files.size(pub) > 0); + } + + @Test + void shortTextSurvivesRoundtrip() throws Exception { + byte[] plain = "Dear readers, today's weather is wet.".getBytes(StandardCharsets.UTF_8); + + assertArrayEquals(plain, roundtrip(plain)); + } + + @Test + void ciphertextDiffersFromPlaintext() throws Exception { + Path in = dir.resolve("in.dat"); + Path enc = dir.resolve("enc.dat"); + byte[] plain = "secret".getBytes(StandardCharsets.UTF_8); + Files.write(in, plain); + + new RunEncrypt().run(Arrays.asList(pub.toString(), in.toString(), enc.toString())); + + byte[] cipherText = Files.readAllBytes(enc); + assertEquals(RunnableBase.KEY_LENGTH / 8, cipherText.length); + assertTrue(!Arrays.equals(plain, cipherText)); + } + + @Test + void largeFileSurvivesRoundtrip() throws Exception { + byte[] plain = new byte[1000]; + new Random(42).nextBytes(plain); + + assertArrayEquals(plain, roundtrip(plain)); + } +}