diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 00000000..26d33521
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/.idea/.name b/.idea/.name
new file mode 100644
index 00000000..ea90b630
--- /dev/null
+++ b/.idea/.name
@@ -0,0 +1 @@
+calc
\ No newline at end of file
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100644
index 00000000..82e319e2
--- /dev/null
+++ b/.idea/compiler.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
new file mode 100644
index 00000000..63e90019
--- /dev/null
+++ b/.idea/encodings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
new file mode 100644
index 00000000..712ab9d9
--- /dev/null
+++ b/.idea/jarRepositories.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/Maven__org_apiguardian_apiguardian_api_1_1_0.xml b/.idea/libraries/Maven__org_apiguardian_apiguardian_api_1_1_0.xml
new file mode 100644
index 00000000..f854ab00
--- /dev/null
+++ b/.idea/libraries/Maven__org_apiguardian_apiguardian_api_1_1_0.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/Maven__org_junit_jupiter_junit_jupiter_5_6_2.xml b/.idea/libraries/Maven__org_junit_jupiter_junit_jupiter_5_6_2.xml
new file mode 100644
index 00000000..71711536
--- /dev/null
+++ b/.idea/libraries/Maven__org_junit_jupiter_junit_jupiter_5_6_2.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/Maven__org_junit_jupiter_junit_jupiter_api_5_6_2.xml b/.idea/libraries/Maven__org_junit_jupiter_junit_jupiter_api_5_6_2.xml
new file mode 100644
index 00000000..ef160d24
--- /dev/null
+++ b/.idea/libraries/Maven__org_junit_jupiter_junit_jupiter_api_5_6_2.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/Maven__org_junit_jupiter_junit_jupiter_engine_5_6_2.xml b/.idea/libraries/Maven__org_junit_jupiter_junit_jupiter_engine_5_6_2.xml
new file mode 100644
index 00000000..fc4c644e
--- /dev/null
+++ b/.idea/libraries/Maven__org_junit_jupiter_junit_jupiter_engine_5_6_2.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/Maven__org_junit_jupiter_junit_jupiter_params_5_6_2.xml b/.idea/libraries/Maven__org_junit_jupiter_junit_jupiter_params_5_6_2.xml
new file mode 100644
index 00000000..45b4af37
--- /dev/null
+++ b/.idea/libraries/Maven__org_junit_jupiter_junit_jupiter_params_5_6_2.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/Maven__org_junit_platform_junit_platform_commons_1_6_2.xml b/.idea/libraries/Maven__org_junit_platform_junit_platform_commons_1_6_2.xml
new file mode 100644
index 00000000..f2699b95
--- /dev/null
+++ b/.idea/libraries/Maven__org_junit_platform_junit_platform_commons_1_6_2.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/Maven__org_junit_platform_junit_platform_engine_1_6_2.xml b/.idea/libraries/Maven__org_junit_platform_junit_platform_engine_1_6_2.xml
new file mode 100644
index 00000000..c13a302c
--- /dev/null
+++ b/.idea/libraries/Maven__org_junit_platform_junit_platform_engine_1_6_2.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/Maven__org_opentest4j_opentest4j_1_2_0.xml b/.idea/libraries/Maven__org_opentest4j_opentest4j_1_2_0.xml
new file mode 100644
index 00000000..fbc1b163
--- /dev/null
+++ b/.idea/libraries/Maven__org_opentest4j_opentest4j_1_2_0.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 00000000..972ec8d7
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 00000000..0fa07049
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 00000000..35eb1ddf
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/calc.iml b/calc.iml
new file mode 100644
index 00000000..8d9aaaf7
--- /dev/null
+++ b/calc.iml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/java/Finance.java b/src/main/java/Finance.java
new file mode 100644
index 00000000..b43d775b
--- /dev/null
+++ b/src/main/java/Finance.java
@@ -0,0 +1,64 @@
+
+import com.h2.BestLoanRates;
+import com.h2.MortgageCalculator;
+import com.h2.SavingsCalculator;
+
+import java.util.Arrays;
+import java.util.Map;
+
+public class Finance {
+
+ public static final String BEST_LOAN_RATES = "bestLoanRates";
+ public static final String SAVINGS_CALCULATOR = "savingsCalculator";
+ public static final String MORTGAGE_CALCULATOR = "mortgageCalculator";
+
+ public static final Map commandsToUsage = Map.of(
+ BEST_LOAN_RATES, "usage: bestLoanRates",
+ SAVINGS_CALCULATOR, "usage: savingsCalculator ",
+ MORTGAGE_CALCULATOR, "usage: mortgageCalculator "
+ );
+
+ public static void main(String[] args) {
+ final String command = args[0];
+ if (!commandsToUsage.containsKey(command)) {
+ System.out.println(command + ": command not found");
+ System.exit(-1);
+ }
+
+ final boolean isValidCommand = validateCommandArguments(args);
+ if (!isValidCommand) {
+ System.out.println(commandsToUsage.get(args[0]));
+ System.exit(-1);
+ }
+
+ executeCommand(command, Arrays.copyOfRange(args, 1, args.length));
+ }
+
+ private static void executeCommand(String command, String[] arguments) {
+ switch (command) {
+ case BEST_LOAN_RATES:
+ System.out.println("Finding best loan rates ..."); // for testing just log
+ BestLoanRates.main(arguments); // for another test call these methods and have executeCommand add to main method
+ return;
+ case SAVINGS_CALCULATOR:
+ System.out.println("Finding your net savings ...");
+ SavingsCalculator.main(arguments);
+ return;
+ case MORTGAGE_CALCULATOR:
+ System.out.println("Finding your monthly payment ...");
+ MortgageCalculator.main(arguments);
+ }
+ }
+
+ private static boolean validateCommandArguments(String[] args) {
+ switch (args[0]) {
+ case BEST_LOAN_RATES:
+ return args.length == 1;
+ case SAVINGS_CALCULATOR:
+ return args.length == 3;
+ case MORTGAGE_CALCULATOR:
+ return args.length == 4;
+ }
+ return false;
+ }
+}
diff --git a/src/main/java/com/h2/App.java b/src/main/java/com/h2/App.java
index 6966d92c..15955ee2 100644
--- a/src/main/java/com/h2/App.java
+++ b/src/main/java/com/h2/App.java
@@ -10,4 +10,19 @@ public static void main( String[] args )
{
System.out.println( "Hello World!" );
}
+
+ public static int doubleTheNumber(int number) {
+ // do it in module02
+ // todo: fix the implementation
+ // return -1;
+ return 2 * number;
+ }
+
+ private static int add(int[] numbers) {
+ var sum = 0;
+ for (int number : numbers) {
+ sum += number;
+ }
+ return sum;
+ }
}
diff --git a/src/main/java/com/h2/BestLoanRates.java b/src/main/java/com/h2/BestLoanRates.java
new file mode 100644
index 00000000..7678c120
--- /dev/null
+++ b/src/main/java/com/h2/BestLoanRates.java
@@ -0,0 +1,39 @@
+package com.h2;
+
+import java.util.Map;
+import java.util.Scanner;
+
+public class BestLoanRates {
+ public final static Map bestRates = Map.of(
+ 1, 5.50f,
+ 2, 3.45f,
+ 3, 2.67f
+ );
+
+ public static void main(String[] args) {
+ Scanner scanner = new Scanner(System.in);
+
+
+ System.out.println("Enter your name");
+ String name = scanner.nextLine();
+ System.out.println("Hello " + name);
+
+ System.out.println("Enter the loan term (in years)");
+ int loanTermInYears = scanner.nextInt();
+ float bestRate = getRates(loanTermInYears);
+ if (bestRate == 0.0f) {
+ System.out.println("No available rates for term: " + loanTermInYears + " years");
+ } else {
+ System.out.println("Best Available Rate: " + getRates(loanTermInYears) + "%");
+ }
+
+ scanner.close();
+ }
+
+ public static float getRates(int loanTermInYears) {
+ if (bestRates.containsKey(loanTermInYears)) {
+ return bestRates.get(loanTermInYears);
+ }
+ return 0.0f;
+ }
+}
diff --git a/src/main/java/com/h2/MortgageCalculator.java b/src/main/java/com/h2/MortgageCalculator.java
new file mode 100644
index 00000000..15c42884
--- /dev/null
+++ b/src/main/java/com/h2/MortgageCalculator.java
@@ -0,0 +1,53 @@
+package com.h2;
+
+import java.text.DecimalFormat;
+
+public class MortgageCalculator {
+ private final long loanAmount;
+ private final int termInYears;
+ private final float annualRate;
+
+ public MortgageCalculator(long loanAmount, int termInYears, float annualRate) {
+ this.loanAmount = loanAmount;
+ this.termInYears = termInYears;
+ this.annualRate = annualRate;
+ }
+
+ private int getNumberOfPayments() {
+ return this.termInYears * 12;
+ }
+
+ private float getMonthlyInterestRate() {
+ float interestRate = annualRate / 100;
+ return interestRate / 12;
+ }
+
+ public double getMonthlyPayment() {
+ long P = loanAmount;
+ float r = getMonthlyInterestRate();
+ int n = getNumberOfPayments();
+
+ System.out.println("P=" + P);
+ System.out.println("r=" + r);
+ System.out.println("n=" + n);
+ double M = P * (((r * Math.pow(1 + r, n))) / ((Math.pow((1 + r), n)) - 1));
+
+ return M;
+ }
+
+ public static void main(String[] args) {
+ if (args.length < 3) {
+ System.out.println("usage: mortgageCalculator ");
+ System.exit(-1);
+ }
+
+ final long loanAmount = Utilities.getLongValue(args[0]);
+ final int termInYears = Utilities.getIntValue(args[1]);
+ final float annualRate = Utilities.getFloatValue(args[2]);
+ final MortgageCalculator c = new MortgageCalculator(loanAmount, termInYears, annualRate);
+ final double monthlyPayment = c.getMonthlyPayment();
+ DecimalFormat df = new DecimalFormat("####0.00");
+
+ System.out.println("Monthly Payment: " + df.format(monthlyPayment));
+ }
+}
diff --git a/src/main/java/com/h2/SavingsCalculator.java b/src/main/java/com/h2/SavingsCalculator.java
new file mode 100644
index 00000000..ac9cc704
--- /dev/null
+++ b/src/main/java/com/h2/SavingsCalculator.java
@@ -0,0 +1,56 @@
+package com.h2;
+
+public class SavingsCalculator {
+ private final float[] credits;
+ private final float[] debits;
+
+ public SavingsCalculator(float[] credits, float[] debits) {
+ this.credits = credits;
+ this.debits = debits;
+ }
+
+ public float calculate() {
+ return sumOfCredits() - sumOfDebits();
+ }
+
+ private float sumOfCredits() {
+ float sum = 0.0f;
+ for (float credit : credits) {
+ sum += credit;
+ }
+ return sum;
+ }
+
+ private float sumOfDebits() {
+ float sum = 0.0f;
+ for (float debit : debits) {
+ sum += debit;
+ }
+ return sum;
+ }
+
+ public static void main(String[] args) {
+ if (args.length < 2) {
+ System.out.println("usage: savingsCalculator ");
+ System.exit(-1);
+ }
+
+ final String[] creditsAsString = args[0].split(",");
+ final String[] debitsAsString = args[1].split(",");
+
+ final float[] credits = new float[creditsAsString.length];
+ final float[] debits = new float[debitsAsString.length];
+
+ for (int i = 0; i < creditsAsString.length; i++) {
+ credits[i] = Utilities.getFloatValue(creditsAsString[i]);
+ }
+
+ for (int i = 0; i < debitsAsString.length; i++) {
+ debits[i] = Utilities.getFloatValue(debitsAsString[i]);
+ }
+
+ final SavingsCalculator calculator = new SavingsCalculator(credits, debits);
+
+ System.out.println("Net Savings = " + calculator.calculate());
+ }
+}
diff --git a/src/main/java/com/h2/Utilities.java b/src/main/java/com/h2/Utilities.java
new file mode 100644
index 00000000..ebfe5676
--- /dev/null
+++ b/src/main/java/com/h2/Utilities.java
@@ -0,0 +1,36 @@
+package com.h2;
+
+public class Utilities {
+ // float, long, int
+
+ public static float getFloatValue(String in) {
+ float out = 0.0f;
+ try {
+ out = Float.parseFloat(in);
+ } catch (NumberFormatException e) {
+ System.out.println(in + " cannot be converted into a 'float' value. Exiting program.");
+ }
+ return out;
+ }
+
+ public static long getLongValue(String in) {
+ long out = 0;
+ try {
+ out = Long.parseLong(in);
+ } catch (NumberFormatException e) {
+ System.out.println(in + " cannot be converted into a 'long' value. Exiting program.");
+ }
+ return out;
+
+ }
+
+ public static int getIntValue(String in) {
+ int out = 0;
+ try {
+ out = Integer.parseInt(in);
+ } catch (NumberFormatException e) {
+ System.out.println(in + " cannot be converted into a 'float' value. Exiting program.");
+ }
+ return out;
+ }
+}
diff --git a/src/test/java/FinanceTest.java b/src/test/java/FinanceTest.java
new file mode 100644
index 00000000..b3799401
--- /dev/null
+++ b/src/test/java/FinanceTest.java
@@ -0,0 +1,112 @@
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.junit.platform.commons.function.Try;
+
+import java.util.Optional;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.platform.commons.util.ReflectionUtils.tryToLoadClass;
+
+public class FinanceTest {
+
+ private final String classToFind = "Finance";
+
+ public Optional> getAppClass() {
+ Try> aClass = tryToLoadClass(classToFind);
+ return aClass.toOptional();
+ }
+
+ @Test
+ public void assertClassExistence() {
+ final Optional> maybeClass = getAppClass();
+ assertTrue(maybeClass.isPresent(), classToFind + " should be present");
+ assertEquals(classToFind, maybeClass.get().getCanonicalName());
+ }
+
+ @Disabled
+ @Test
+ public void testCommandsToUsage() {
+ /*
+ * 1. Existence of field
+ * 2. isPublic, isFinal, isStatic, isMap (type)
+ * 3. Has 3 entries
+ * 4. Test all entries
+ */
+ }
+
+ @Disabled
+ @Test
+ public void testMainCommandInput() {
+ /*
+ * 1. Pass command from the commandsToUsage
+ * 2. Test invalid command
+ */
+ }
+
+ @Disabled
+ @Test
+ public void testValidateCommandArgumentsExistence() {
+ /*
+ * 1. Method exists
+ * 2. isStatic, isPrivate, returns boolean
+ * 3. accepts String[] parameter
+ */
+ }
+
+ @Disabled
+ @Test
+ public void testValidateCommandArgumentsCorrectness() {
+ /*
+ * 1. Send all 3 valid argument[0] with different rest of argument length
+ * 2. Send invalid args[0] and assert that it should return false;
+ */
+ }
+
+ @Disabled
+ @Test
+ public void testMainWithValidCommandInvalidUsage() {
+ /*
+ * 1. test all valid command names with invalid arguments lengths
+ */
+ }
+
+ @Disabled
+ @Test
+ public void testCommandConstantFields() {
+ /*
+ * 1. Existence of BEST_LOAN_RATES, SAVINGS_CALCULATOR, MORTGAGE_CALCULATOR
+ * 2. isPublic, isStatic, isFinal
+ * 3. Right values for each field
+ */
+ }
+
+ @Disabled
+ @Test
+ public void testExecuteCommandExistence() {
+ /*
+ * 1. Method exists
+ * 2. isPrivate isStatic, returns nothing
+ * 3. Has parameters
+ */
+ }
+
+ @Disabled
+ @Test
+ public void testExecuteCommandExistenceForCorrectness() {
+ /*
+ * 1. Test the validity for correct statement printed on console based on command
+ */
+ }
+
+ @Disabled
+ @Test
+ public void testMainWithValidCommandUsage() {
+ /*
+ * 1. Test with bestLoanRates 1
+ * 2. Test with savingsCalculator 20.0,30.0 10.0,5.0
+ * 3. Test with mortgageCalculator 264000 30 3.74f
+ */
+ }
+
+}
diff --git a/src/test/java/com/h2/AppTest.java b/src/test/java/com/h2/AppTest.java
index 38e8e89d..57b43f2f 100644
--- a/src/test/java/com/h2/AppTest.java
+++ b/src/test/java/com/h2/AppTest.java
@@ -1,20 +1,56 @@
package com.h2;
+import org.junit.jupiter.api.Test;
+import org.junit.platform.commons.function.Try;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+import java.util.Optional;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.platform.commons.util.ReflectionUtils.*;
-import org.junit.jupiter.api.Test;
+public class AppTest {
+ private final String classToFind = "com.h2.App";
+
+ public Optional> getAppClass() {
+ Try> aClass = tryToLoadClass(classToFind);
+ return aClass.toOptional();
+ }
+
+ @Test
+ public void assertClassExistence() {
+ final Optional> maybeClass = getAppClass();
+ assertTrue(maybeClass.isPresent(), classToFind + " should be present");
+ assertEquals(classToFind, maybeClass.get().getCanonicalName());
+ }
+
+ @Test
+ public void assertPrivateMethodExistence() {
+ final String methodName = "add";
+ final Optional> maybeClass = getAppClass();
+ Class> aClass = maybeClass.get();
+ Optional maybeMethod = findMethod(aClass, methodName, int[].class);
+ assertTrue(maybeMethod.isPresent(), methodName + " should be present in " + aClass.getCanonicalName());
+
+ final Method method = maybeMethod.get();
+ assertTrue(isPrivate(method), methodName + " should be private");
+
+ assertEquals(int.class, method.getReturnType(), methodName + " should return type should be 'int'");
+
+ Parameter[] parameters = method.getParameters();
+ assertEquals(1, parameters.length, methodName + " should have 1 parameter");
+ assertEquals(int[].class, parameters[0].getType(), methodName + " parameter should be of type 'int[]'");
+
+ assertTrue(isStatic(method), methodName + "should be static method");
+ assertTrue(isPrivate(method), methodName + "should be private method");
+ }
-/**
- * Unit test for simple App.
- */
-public class AppTest
-{
- /**
- * Rigorous Test :-)
- */
@Test
- public void shouldAnswerWithTrue()
- {
- assertTrue( true );
+ public void testDoubleTheNumber() {
+ for (int i = 1; i < 10; i++) {
+ assertEquals(2 * i, App.doubleTheNumber(i), i + " should be " + 2 * i);
+ }
}
}
diff --git a/src/test/java/com/h2/BestLoanRatesTest.java b/src/test/java/com/h2/BestLoanRatesTest.java
new file mode 100644
index 00000000..9f4c5dea
--- /dev/null
+++ b/src/test/java/com/h2/BestLoanRatesTest.java
@@ -0,0 +1,158 @@
+package com.h2;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.platform.commons.function.Try;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.platform.commons.util.ReflectionUtils.*;
+
+public class BestLoanRatesTest {
+ private final String classToFind = "com.h2.BestLoanRates";
+
+ private final InputStream systemIn = System.in;
+ private final PrintStream systemOut = System.out;
+
+ private ByteArrayInputStream testIn;
+ private ByteArrayOutputStream testOut;
+
+ @BeforeEach
+ public void setUpOutput() {
+ testOut = new ByteArrayOutputStream();
+ System.setOut(new PrintStream(testOut));
+ }
+
+ private void provideInput(String data) {
+ testIn = new ByteArrayInputStream(data.getBytes());
+ System.setIn(testIn);
+ }
+
+ private String getOutput() {
+ return testOut.toString();
+ }
+
+ @AfterEach
+ public void restoreSystemInputOutput() {
+ System.setIn(systemIn);
+ System.setOut(systemOut);
+ }
+
+ public Optional> getAppClass() {
+ Try> aClass = tryToLoadClass(classToFind);
+ return aClass.toOptional();
+ }
+
+ @Test
+ public void assertClassExistence() {
+ final Optional> maybeClass = getAppClass();
+ assertTrue(maybeClass.isPresent(), classToFind + " should be present");
+ assertEquals(classToFind, maybeClass.get().getCanonicalName());
+ }
+
+ @Test
+ public void testName() {
+ final String name = "H2";
+ final int age = 32;
+ final String testString = name + "\n" + age;
+ provideInput(testString);
+
+ BestLoanRates.main(new String[0]);
+
+ List outputList = Arrays.stream(getOutput().split("\n")).collect(Collectors.toList());
+ assertEquals("Enter your name", outputList.get(0));
+ assertEquals("Hello " + name, outputList.get(1));
+ }
+
+ @Test
+ public void testGetRatesMethod() {
+ String getRates = "getRates";
+ final Optional> maybeClass = getAppClass();
+ assertTrue(maybeClass.isPresent(), classToFind + " should be present");
+ Class> c = maybeClass.get();
+ List methods = Arrays.stream(c.getDeclaredMethods())
+ .filter(m -> m.getName().equals(getRates))
+ .collect(Collectors.toList());
+
+ assertEquals(1, methods.size(), getRates + " must be defined as a method in CommandLineApp");
+
+ final Method method = methods.get(0);
+ assertEquals(float.class, method.getReturnType(), getRates + " must return 'float' as the return type");
+ assertTrue(isStatic(method), getRates + " must be a static method");
+ assertTrue(isPublic(method), getRates + " must be a public method");
+ }
+
+ @Test
+ public void testBestRatesExistence() throws IllegalAccessException {
+ String bestRates = "bestRates";
+ final Optional> maybeClass = getAppClass();
+ assertTrue(maybeClass.isPresent(), classToFind + " should be present");
+ Class> c = maybeClass.get();
+ List fields = Arrays.stream(c.getFields())
+ .filter(f -> f.getName().equals(bestRates))
+ .collect(Collectors.toList());
+
+ assertEquals(1, fields.size(), bestRates + " must be defined as a field in CommandLineApp.");
+
+ Field field = fields.get(0);
+ assertEquals(Map.class, field.getType(), bestRates + " must be of type 'Map'");
+
+ field.setAccessible(true);
+ Map fieldValues = (Map) field.get(BestLoanRates.class);
+
+ assertEquals(3, fieldValues.size(), bestRates + " should have 3 entries");
+ assertEquals(5.50f, fieldValues.get(1), bestRates + " should return '5.50f' for key '1'");
+ assertEquals(3.45f, fieldValues.get(2), bestRates + " should return '3.45f' for key '2'");
+ assertEquals(2.67f, fieldValues.get(3), bestRates + " should return '2.67f' for key '3'");
+ }
+
+ @Test
+ public void testNameAndValidLoanTerm() {
+ final String name = "H2";
+ final int loanTermInYears = 2;
+ final String testString = name + "\n" + loanTermInYears;
+ provideInput(testString);
+
+ BestLoanRates.main(new String[0]);
+
+ List outputList = Arrays.stream(getOutput().split("\n")).collect(Collectors.toList());
+ assertEquals("Enter your name", outputList.get(0));
+ assertEquals("Hello " + name, outputList.get(1));
+
+ assertEquals("Enter the loan term (in years)", outputList.get(2));
+ assertEquals("Best Available Rate: " + BestLoanRates.bestRates.get(loanTermInYears) + "%", outputList.get(3));
+ assertEquals(4, outputList.size());
+ }
+
+ @Test
+ public void testNameAndInValidLoanTerm() {
+ final String name = "H2";
+ final int loanTermInYears = 20;
+ final String testString = name + "\n" + loanTermInYears;
+ provideInput(testString);
+
+ BestLoanRates.main(new String[0]);
+
+ List outputList = Arrays.stream(getOutput().split("\n")).collect(Collectors.toList());
+ assertEquals("Enter your name", outputList.get(0));
+ assertEquals("Hello " + name, outputList.get(1));
+
+ assertEquals("Enter the loan term (in years)", outputList.get(2));
+ assertEquals("No available rates for term: " + loanTermInYears + " years", outputList.get(3));
+ assertEquals(4, outputList.size());
+ }
+
+}
diff --git a/src/test/java/com/h2/MortgageCalculatorTest.java b/src/test/java/com/h2/MortgageCalculatorTest.java
new file mode 100644
index 00000000..c867ee23
--- /dev/null
+++ b/src/test/java/com/h2/MortgageCalculatorTest.java
@@ -0,0 +1,95 @@
+package com.h2;
+
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+public class MortgageCalculatorTest {
+
+ @Disabled
+ @Test
+ public void testMortgageCalculatorExists() {
+ }
+
+ @Disabled
+ @Test
+ public void testExistenceOfPrivateFields() {
+ }
+
+ @Disabled
+ @Test
+ public void testConstructor() {
+ }
+
+ @Disabled
+ @Test
+ public void testFieldsValueSetWhenConstructorCalled() {
+ }
+
+ @Disabled
+ @Test
+ public void testExistenceOfNumberOfPayments() {
+ /*
+ 1. Existence
+ 2. Private
+ 3. Return Type
+ */
+ }
+
+ @Disabled
+ @Test
+ public void testNumberOfPaymentsCorrectness() {
+ /*
+ 1. Correct output based on termInYears
+ */
+ }
+
+ @Disabled
+ @Test
+ public void testExistenceOfMonthlyInterestRate() {
+ /*
+ 1. Existence
+ 2. Private
+ 3. Return Type
+ */
+ }
+
+ @Disabled
+ @Test
+ public void testMonthlyInterestRateCorrectness() {
+ /*
+ 1. Correct output based on annualRate
+ */
+ }
+
+ @Disabled
+ @Test
+ public void testExistenceOfMonthlyPayment() {
+ /*
+ 1. Existence
+ 2. Private
+ 3. Return Type
+ */
+ }
+
+ @Disabled
+ @Test
+ public void testMonthlyPaymentCorrectness() {
+ /*
+ 1. Correct output based on loanAmount, termInTYears, and annualRate
+ */
+ }
+
+ @Disabled
+ @Test
+ public void testMainMethodPrintsCorrectOutput() {
+ /*
+ 1. MortgageCalculator.main.
+ */
+ }
+
+ @Disabled
+ @Test
+ public void testMainMethodForCorrectArgumentLengths() {
+
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/com/h2/SavingsCalculatorTest.java b/src/test/java/com/h2/SavingsCalculatorTest.java
new file mode 100644
index 00000000..d0bce46c
--- /dev/null
+++ b/src/test/java/com/h2/SavingsCalculatorTest.java
@@ -0,0 +1,228 @@
+package com.h2;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.junit.platform.commons.function.Try;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.junit.platform.commons.util.ReflectionUtils.*;
+
+@SuppressWarnings("unused")
+public class SavingsCalculatorTest {
+ private final ByteArrayOutputStream out = new ByteArrayOutputStream();
+ private final PrintStream originalOut = System.out;
+
+ @BeforeEach
+ public void setStreams() {
+ System.setOut(new PrintStream(out));
+ }
+
+ @AfterEach
+ public void restoreInitialStreams() {
+ System.setOut(originalOut);
+ }
+
+ private final String classToFind = "com.h2.SavingsCalculator";
+
+ public Optional> getAppClass() {
+ final Try> aClass = tryToLoadClass(classToFind);
+ return aClass.toOptional();
+ }
+
+ @Test
+ public void testSavingsCalculatorExists() {
+ final Optional> maybeSavingsCalculator = getAppClass();
+ assertTrue(maybeSavingsCalculator.isPresent(), classToFind + " must be created");
+ }
+
+ @Test
+ public void testExistenceOfPrivateFields() {
+ final Optional> maybeSavingsCalculator = getAppClass();
+ assertTrue(maybeSavingsCalculator.isPresent());
+ final Class> savingsCalculator = maybeSavingsCalculator.get();
+ final Set fieldNames = new HashSet<>(Arrays.asList("credits", "debits"));
+
+ final Field[] declaredFields = savingsCalculator.getDeclaredFields();
+ assertEquals(2, declaredFields.length, "2 fields should be available in " + classToFind);
+
+ for (final Field field : declaredFields) {
+ assertTrue(isPrivate(field), field.getName() + " should be declared private");
+ assertEquals(float[].class, field.getType(), field.getName() + " should be of type 'float[]'");
+ assertTrue(fieldNames.contains(field.getName()), field.getName() + " is not a valid name. It should be either 'credits' or 'debits'");
+ }
+ }
+
+ @Test
+ public void testConstructor() {
+ final Optional> maybeSavingsCalculator = getAppClass();
+ assertTrue(maybeSavingsCalculator.isPresent());
+ final Class> savingsCalculator = maybeSavingsCalculator.get();
+ final Constructor>[] constructors = savingsCalculator.getDeclaredConstructors();
+
+ assertEquals(1, constructors.length, classToFind + " should have 1 constructor");
+
+ final Constructor> constructor = constructors[0];
+ assertTrue(isPublic(constructor), "constructor must be declared 'public'");
+ assertEquals(2, constructor.getParameterCount(), "Constructor should have 2 parameters");
+
+ for (final Parameter parameter : constructor.getParameters()) {
+ assertEquals(float[].class, parameter.getType(), "Constructor parameter should be of type 'float[]'");
+ }
+ }
+
+ @Test
+ public void testFieldsValueSetWhenConstructorCalled() throws IllegalAccessException {
+ float[] credits = new float[]{10.0f, 20.0f};
+ float[] debits = new float[]{5.0f};
+ final SavingsCalculator calculator = new SavingsCalculator(credits, debits);
+
+ final Class> clazz = calculator.getClass();
+ final Field[] fields = clazz.getDeclaredFields();
+
+ for (final Field field : fields) {
+ field.setAccessible(true);
+ float[] fieldValues = (float[]) field.get(calculator);
+ if (field.getName().equals("credits")) {
+ assertArrayEquals(credits, fieldValues, "credits parameter should set the value in class field name 'credits'");
+ } else if (field.getName().equals("debits")) {
+ assertArrayEquals(debits, fieldValues, "debits parameter should set the value in class field name 'debits'");
+ }
+ }
+ }
+
+ @Test
+ public void testCalculateExists() {
+ final String methodName = "calculate";
+ final Optional> maybeSavingsCalculator = getAppClass();
+ assertTrue(maybeSavingsCalculator.isPresent());
+ final Class> savingsCalculator = maybeSavingsCalculator.get();
+
+ final Method[] methods = savingsCalculator.getDeclaredMethods();
+ final List filteredMethod = Arrays.stream(methods).filter(method -> method.getName().equals(methodName)).collect(Collectors.toList());
+
+ assertEquals(1, filteredMethod.size(), classToFind + " should contain a method called '" + methodName + "'");
+
+ final Method calculate = filteredMethod.get(0);
+ assertTrue(isPublic(calculate), methodName + " must be declared as 'public'");
+ assertEquals(float.class, calculate.getReturnType(), methodName + " method must return a value of type 'float'");
+ }
+
+ @Test
+ public void testSumOfCreditsExists() {
+ final String methodName = "sumOfCredits";
+
+ final Optional> maybeSavingsCalculator = getAppClass();
+ assertTrue(maybeSavingsCalculator.isPresent());
+ final Class> savingsCalculator = maybeSavingsCalculator.get();
+
+ final Method[] methods = savingsCalculator.getDeclaredMethods();
+ final List filteredMethod = Arrays.stream(methods).filter(method -> method.getName().equals(methodName)).collect(Collectors.toList());
+
+ assertEquals(1, filteredMethod.size(), classToFind + " should contain a method called '" + methodName + "'");
+
+ final Method sumOfCredits = filteredMethod.get(0);
+ assertTrue(isPrivate(sumOfCredits), methodName + " must be declared as 'private'");
+ assertEquals(float.class, sumOfCredits.getReturnType(), methodName + " method must return a value of type 'float'");
+ }
+
+ @Test
+ public void testSumOfCreditsWorksCorrectly() {
+ final Optional> maybeSavingsCalculator = getAppClass();
+ assertTrue(maybeSavingsCalculator.isPresent());
+ final Class> savingsCalculator = maybeSavingsCalculator.get();
+
+ float[] credits = new float[]{10.0f, 20.0f};
+ float[] debits = new float[]{5.0f};
+
+ final SavingsCalculator calculator = newInstance(SavingsCalculator.class, credits, debits);
+
+ final String methodName = "sumOfCredits";
+ final Optional maybeMethod = findMethod(SavingsCalculator.class, methodName);
+ assertTrue(maybeMethod.isPresent());
+ final Method sumOfCredits = maybeMethod.get();
+ final float result = (float) invokeMethod(sumOfCredits, calculator);
+
+ assertEquals(30.0f, result, methodName + " is not returning sum of credits.");
+ }
+
+ @Test
+ public void testSumOfDebitsExists() {
+ final String methodName = "sumOfDebits";
+
+ final Optional> maybeSavingsCalculator = getAppClass();
+ assertTrue(maybeSavingsCalculator.isPresent());
+ final Class> savingsCalculator = maybeSavingsCalculator.get();
+
+ final Method[] methods = savingsCalculator.getDeclaredMethods();
+ final List filteredMethod = Arrays.stream(methods).filter(method -> method.getName().equals(methodName)).collect(Collectors.toList());
+
+ assertEquals(1, filteredMethod.size(), classToFind + " should contain a method called '" + methodName + "'");
+
+ final Method sumOfCredits = filteredMethod.get(0);
+ assertTrue(isPrivate(sumOfCredits), methodName + " must be declared as 'private'");
+ assertEquals(float.class, sumOfCredits.getReturnType(), methodName + " method must return a value of type 'float'");
+
+ }
+
+ @Test
+ public void testSumOfDebitsWorksCorrectly() {
+ final Optional> maybeSavingsCalculator = getAppClass();
+ assertTrue(maybeSavingsCalculator.isPresent());
+ final Class> savingsCalculator = maybeSavingsCalculator.get();
+
+ float[] credits = new float[]{10.0f, 20.0f};
+ float[] debits = new float[]{5.0f, 10.f};
+
+ final SavingsCalculator calculator = newInstance(SavingsCalculator.class, credits, debits);
+
+ final String methodName = "sumOfDebits";
+ final Optional maybeMethod = findMethod(SavingsCalculator.class, methodName);
+ assertTrue(maybeMethod.isPresent());
+ final Method sumOfDebits = maybeMethod.get();
+ final float result = (float) invokeMethod(sumOfDebits, calculator);
+
+ assertEquals(15.0f, result, methodName + " is not returning sum of credits.");
+ }
+
+ @Test
+ public void testCalculateWorksCorrectly() {
+ final Optional> maybeSavingsCalculator = getAppClass();
+ assertTrue(maybeSavingsCalculator.isPresent());
+ final Class> savingsCalculator = maybeSavingsCalculator.get();
+
+ float[] credits = new float[]{10.0f, 20.0f};
+ float[] debits = new float[]{5.0f, 10.f};
+
+ final SavingsCalculator calculator = newInstance(SavingsCalculator.class, credits, debits);
+ final float result = calculator.calculate();
+
+ assertEquals((10.0f + 20.0f) - (5.0f + 10.0f), result, "calculate method is not returning sum of credits minus sum of debits");
+ }
+
+ @Test
+ public void testMainMethodPrintsCorrectOutput() {
+ final String credits = "10.0,20.0";
+ final String debits = "5.0,20.0";
+
+ SavingsCalculator.main(new String[]{credits, debits});
+ assertEquals("Net Savings = 5.0\n", out.toString());
+ }
+
+ @Disabled
+ @Test
+ public void testMainMethodForCorrectArgumentLengths() {
+
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/com/h2/UtilitiesTest.java b/src/test/java/com/h2/UtilitiesTest.java
new file mode 100644
index 00000000..4acd3137
--- /dev/null
+++ b/src/test/java/com/h2/UtilitiesTest.java
@@ -0,0 +1,72 @@
+package com.h2;
+
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.junit.platform.commons.function.Try;
+
+import java.util.Optional;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.platform.commons.util.ReflectionUtils.tryToLoadClass;
+
+public class UtilitiesTest {
+ private final String classToFind = "com.h2.Utilities";
+
+ public Optional> getAppClass() {
+ Try> aClass = tryToLoadClass(classToFind);
+ return aClass.toOptional();
+ }
+
+ @Test
+ public void assertClassExistence() {
+ final Optional> maybeClass = getAppClass();
+ assertTrue(maybeClass.isPresent(), classToFind + " should be present");
+ assertEquals(classToFind, maybeClass.get().getCanonicalName());
+ }
+
+ @Disabled
+ @Test
+ public void testGetFloatValueExistence() {
+ /*
+ * 1. Method exists
+ * 2. isPublic, isStatic, returns float, takes String param
+ */
+ }
+
+ @Disabled
+ @Test
+ public void testGetIntValueExistence() {
+ /*
+ * 1. Method exists
+ * 2. isPublic, isStatic, returns int, takes String param
+ */
+ }
+
+ @Disabled
+ @Test
+ public void testGetLongValueExistence() {
+ /*
+ * 1. Method exists
+ * 2. isPublic, isStatic, returns long, takes String param
+ */
+ }
+
+ @Disabled
+ @Test
+ public void testGetFloatValueCorrectness() {
+
+ }
+
+ @Disabled
+ @Test
+ public void testGetLongValueCorrectness() {
+
+ }
+
+ @Disabled
+ @Test
+ public void testGetIntValueCorrectness() {
+
+ }
+}