import static org.junit.jupiter.api.Assertions.*;

import java.util.Scanner;

import org.junit.jupiter.api.Test;

/**
 * Test class for the RobustPaycheckStorage program.
 * 
 * @author Sarah Heckman
 * @author Jessica Young Schmidt
 */
public class RobustPaycheckStorageTest {
    /** Delta for comparing doubles */
    public static final double DELTA = 0.0001;

    /**
     * Test the constants for correct value
     */
    @Test
    public void testConstants() {
        assertEquals(1, RobustPaycheckStorage.LEVEL_1, "Level 1");
        assertEquals(2, RobustPaycheckStorage.LEVEL_2, "Level 2");
        assertEquals(3, RobustPaycheckStorage.LEVEL_3, "Level 3");
        assertEquals(1900, RobustPaycheckStorage.LEVEL_1_PAY_RATE,
                "Level 1 pay rate");
        assertEquals(2250, RobustPaycheckStorage.LEVEL_2_PAY_RATE,
                "Level 2 pay rate");
        assertEquals(2575, RobustPaycheckStorage.LEVEL_3_PAY_RATE,
                "Level 3 pay rate");
    }

    /**
     * Test the RobustPaycheckStorage.getPayRate() method.
     */
    @Test
    public void testGetPayRate() {
        // Test level 1
        assertEquals(RobustPaycheckStorage.LEVEL_1_PAY_RATE,
                RobustPaycheckStorage.getPayRate(RobustPaycheckStorage.LEVEL_1),
                "RobustPaycheckStorage.getPayRate(RobustPaycheckStorage.LEVEL_1)");

        // Test level 2
        assertEquals(RobustPaycheckStorage.LEVEL_2_PAY_RATE,
                RobustPaycheckStorage.getPayRate(RobustPaycheckStorage.LEVEL_2),
                "RobustPaycheckStorage.getPayRate(RobustPaycheckStorage.LEVEL_2)");

        // Test level 3
        assertEquals(RobustPaycheckStorage.LEVEL_3_PAY_RATE,
                RobustPaycheckStorage.getPayRate(RobustPaycheckStorage.LEVEL_3),
                "RobustPaycheckStorage.getPayRate(RobustPaycheckStorage.LEVEL_3)");

        // Test level 0 - invalid lower boundary
        Exception exception = assertThrows(IllegalArgumentException.class,
            () -> RobustPaycheckStorage.getPayRate(0), "Testing level 0");
        assertEquals("Invalid Level", exception.getMessage(),
                "Testing level 0 - exception message");

        // Test level 4 - invalid upper boundary
        exception = assertThrows(IllegalArgumentException.class,
            () -> RobustPaycheckStorage.getPayRate(4), "Testing level 4");
        assertEquals("Invalid Level", exception.getMessage(),
                "Testing level 4 - exception message");

    }

    /**
     * Test the RobustPaycheckStorage.calculateRegularPay() method.
     */
    @Test
    public void testCalculateRegularPay() {

        // Less than 40 hours
        // Regular Level 1 36 hours
        assertEquals(68400,
                RobustPaycheckStorage.calculateRegularPay(
                        RobustPaycheckStorage.LEVEL_1_PAY_RATE, 36),
                "RobustPaycheckStorage.calculateRegularPay("
                        + "RobustPaycheckStorage.LEVEL_1_PAY_RATE, 36)");

        // Regular Level 2 36 hours
        assertEquals(81000,
                RobustPaycheckStorage.calculateRegularPay(
                        RobustPaycheckStorage.LEVEL_2_PAY_RATE, 36),
                "RobustPaycheckStorage.calculateRegularPay("
                        + "RobustPaycheckStorage.LEVEL_2_PAY_RATE, 36)");

        // Regular Level 3 36 hours
        assertEquals(92700,
                RobustPaycheckStorage.calculateRegularPay(
                        RobustPaycheckStorage.LEVEL_3_PAY_RATE, 36),
                "RobustPaycheckStorage.calculateRegularPay("
                        + "RobustPaycheckStorage.LEVEL_3_PAY_RATE, 36)");

        // Over 40 hours
        // Regular Level 1 46 hours
        assertEquals(76000,
                RobustPaycheckStorage.calculateRegularPay(
                        RobustPaycheckStorage.LEVEL_1_PAY_RATE, 46),
                "RobustPaycheckStorage.calculateRegularPay("
                        + "RobustPaycheckStorage.LEVEL_1_PAY_RATE, 46)");

        // Regular Level 2 46 hours
        assertEquals(90000,
                RobustPaycheckStorage.calculateRegularPay(
                        RobustPaycheckStorage.LEVEL_2_PAY_RATE, 46),
                "RobustPaycheckStorage.calculateRegularPay("
                        + "RobustPaycheckStorage.LEVEL_2_PAY_RATE, 46)");

        // Regular Level 3 46 hours
        assertEquals(103000,
                RobustPaycheckStorage.calculateRegularPay(
                        RobustPaycheckStorage.LEVEL_3_PAY_RATE, 46),
                "RobustPaycheckStorage.calculateRegularPay("
                        + "RobustPaycheckStorage.LEVEL_3_PAY_RATE, 46)");

        // Fractional hours less than 40
        // Regular Level 1 36.5 hours
        assertEquals(69350,
                RobustPaycheckStorage.calculateRegularPay(
                        RobustPaycheckStorage.LEVEL_1_PAY_RATE, 36.5),
                "RobustPaycheckStorage.calculateRegularPay("
                        + "RobustPaycheckStorage.LEVEL_1_PAY_RATE, 36.5)");

        // Regular Level 2 36.5 hours
        assertEquals(82125,
                RobustPaycheckStorage.calculateRegularPay(
                        RobustPaycheckStorage.LEVEL_2_PAY_RATE, 36.5),
                "RobustPaycheckStorage.calculateRegularPay("
                        + "RobustPaycheckStorage.LEVEL_2_PAY_RATE, 36.5)");

        // Regular Level 3 36.5 hours
        assertEquals(93987,
                RobustPaycheckStorage.calculateRegularPay(
                        RobustPaycheckStorage.LEVEL_3_PAY_RATE, 36.5),
                "RobustPaycheckStorage.calculateRegularPay("
                        + "RobustPaycheckStorage.LEVEL_3_PAY_RATE, 36.5)");

        // Test a payrate other than a given constant Regular
        assertEquals(34950,
                RobustPaycheckStorage.calculateRegularPay(1376, 25.4),
                "RobustPaycheckStorage.calculateRegularPay(1376, 25.4)");

        // Testing boundary
        // Less than 40 hours
        // Regular Level 1 39 hours
        assertEquals(74100,
                RobustPaycheckStorage.calculateRegularPay(
                        RobustPaycheckStorage.LEVEL_1_PAY_RATE, 39),
                "RobustPaycheckStorage.calculateRegularPay("
                        + "RobustPaycheckStorage.LEVEL_1_PAY_RATE, 39)");

        // Regular Level 2 39 hours
        assertEquals(87750,
                RobustPaycheckStorage.calculateRegularPay(
                        RobustPaycheckStorage.LEVEL_2_PAY_RATE, 39),
                "RobustPaycheckStorage.calculateRegularPay("
                        + "RobustPaycheckStorage.LEVEL_2_PAY_RATE, 39)");

        // Regular Level 3 39 hours
        assertEquals(100425,
                RobustPaycheckStorage.calculateRegularPay(
                        RobustPaycheckStorage.LEVEL_3_PAY_RATE, 39),
                "RobustPaycheckStorage.calculateRegularPay("
                        + "RobustPaycheckStorage.LEVEL_3_PAY_RATE, 39)");

        // Regular Level 1 40 hours
        assertEquals(76000,
                RobustPaycheckStorage.calculateRegularPay(
                        RobustPaycheckStorage.LEVEL_1_PAY_RATE, 40),
                "RobustPaycheckStorage.calculateRegularPay("
                        + "RobustPaycheckStorage.LEVEL_1_PAY_RATE, 40)");

        // Regular Level 2 40 hours
        assertEquals(90000,
                RobustPaycheckStorage.calculateRegularPay(
                        RobustPaycheckStorage.LEVEL_2_PAY_RATE, 40),
                "RobustPaycheckStorage.calculateRegularPay("
                        + "RobustPaycheckStorage.LEVEL_2_PAY_RATE, 40)");

        // Regular Level 3 40 hours
        assertEquals(103000,
                RobustPaycheckStorage.calculateRegularPay(
                        RobustPaycheckStorage.LEVEL_3_PAY_RATE, 40),
                "RobustPaycheckStorage.calculateRegularPay("
                        + "RobustPaycheckStorage.LEVEL_3_PAY_RATE, 40)");

        // Over 40 hours
        // Regular Level 1 41 hours
        assertEquals(76000,
                RobustPaycheckStorage.calculateRegularPay(
                        RobustPaycheckStorage.LEVEL_1_PAY_RATE, 41),
                "RobustPaycheckStorage.calculateRegularPay("
                        + "RobustPaycheckStorage.LEVEL_1_PAY_RATE, 41)");

        // Regular Level 2 41 hours
        assertEquals(90000,
                RobustPaycheckStorage.calculateRegularPay(
                        RobustPaycheckStorage.LEVEL_2_PAY_RATE, 41),
                "RobustPaycheckStorage.calculateRegularPay("
                        + "RobustPaycheckStorage.LEVEL_2_PAY_RATE, 41)");

        // Regular Level 3 41 hours
        assertEquals(103000,
                RobustPaycheckStorage.calculateRegularPay(
                        RobustPaycheckStorage.LEVEL_3_PAY_RATE, 41),
                "RobustPaycheckStorage.calculateRegularPay("
                        + "RobustPaycheckStorage.LEVEL_3_PAY_RATE, 41)");

        // Test negative hours
        Exception exception = assertThrows(IllegalArgumentException.class,
            () -> RobustPaycheckStorage.calculateRegularPay(
                        RobustPaycheckStorage.LEVEL_3_PAY_RATE, -1),
                "Testing negative hours worked");
        assertEquals("Negative pay rate and/or hours worked",
                exception.getMessage(),
                "Testing negative hours worked - exception message");

        // Test negative pay rate
        exception = assertThrows(IllegalArgumentException.class,
            () -> RobustPaycheckStorage.calculateRegularPay(-20, 20),
                "Testing negative pay rate");
        assertEquals("Negative pay rate and/or hours worked",
                exception.getMessage(),
                "Testing negative pay rate - exception message");
    }

    /**
     * Test the RobustPaycheckStorage.calculateOvertimePay() method.
     */
    @Test
    public void testCalculateOvertimePay() {

        // Less than 40 hours
        // Overtime Level 1 36 hours
        assertEquals(0,
                RobustPaycheckStorage.calculateOvertimePay(
                        RobustPaycheckStorage.LEVEL_1_PAY_RATE, 36),
                "RobustPaycheckStorage.calculateOvertimePay("
                        + "RobustPaycheckStorage.LEVEL_1_PAY_RATE, 36)");

        // Overtime Level 2 36 hours
        assertEquals(0,
                RobustPaycheckStorage.calculateOvertimePay(
                        RobustPaycheckStorage.LEVEL_2_PAY_RATE, 36),
                "RobustPaycheckStorage.calculateOvertimePay("
                        + "RobustPaycheckStorage.LEVEL_2_PAY_RATE, 36)");

        // Overtime Level 3 36 hours
        assertEquals(0,
                RobustPaycheckStorage.calculateOvertimePay(
                        RobustPaycheckStorage.LEVEL_3_PAY_RATE, 36),
                "RobustPaycheckStorage.calculateOvertimePay("
                        + "RobustPaycheckStorage.LEVEL_3_PAY_RATE, 36)");

        // Over 40 hours
        // Overtime Level 1 46 hours
        assertEquals(17100,
                RobustPaycheckStorage.calculateOvertimePay(
                        RobustPaycheckStorage.LEVEL_1_PAY_RATE, 46),
                "RobustPaycheckStorage.calculateOvertimePay("
                        + "RobustPaycheckStorage.LEVEL_1_PAY_RATE, 46)");

        // Overtime Level 2 46 hours
        assertEquals(20250,
                RobustPaycheckStorage.calculateOvertimePay(
                        RobustPaycheckStorage.LEVEL_2_PAY_RATE, 46),
                "RobustPaycheckStorage.calculateOvertimePay("
                        + "RobustPaycheckStorage.LEVEL_2_PAY_RATE, 46)");

        // Overtime Level 3 46 hours
        assertEquals(23172,
                RobustPaycheckStorage.calculateOvertimePay(
                        RobustPaycheckStorage.LEVEL_3_PAY_RATE, 46),
                "RobustPaycheckStorage.calculateOvertimePay("
                        + "RobustPaycheckStorage.LEVEL_3_PAY_RATE, 46)");

        // Fractional hours less than 40
        // Overtime Level 1 36.5 hours
        assertEquals(0,
                RobustPaycheckStorage.calculateOvertimePay(
                        RobustPaycheckStorage.LEVEL_1_PAY_RATE, 36.5),
                "RobustPaycheckStorage.calculateOvertimePay("
                        + "RobustPaycheckStorage.LEVEL_1_PAY_RATE, 36.5)");

        // Overtime Level 2 36.5 hours
        assertEquals(0,
                RobustPaycheckStorage.calculateOvertimePay(
                        RobustPaycheckStorage.LEVEL_2_PAY_RATE, 36.5),
                "RobustPaycheckStorage.calculateOvertimePay("
                        + "RobustPaycheckStorage.LEVEL_2_PAY_RATE, 36.5)");

        // Overtime Level 3 36.5 hours
        assertEquals(0,
                RobustPaycheckStorage.calculateOvertimePay(
                        RobustPaycheckStorage.LEVEL_3_PAY_RATE, 36.5),
                "RobustPaycheckStorage.calculateOvertimePay("
                        + "RobustPaycheckStorage.LEVEL_3_PAY_RATE, 36.5)");

        // Fractional hours over 40 hours
        // Overtime Level 1 46.5 hours
        assertEquals(18525,
                RobustPaycheckStorage.calculateOvertimePay(
                        RobustPaycheckStorage.LEVEL_1_PAY_RATE, 46.5),
                "RobustPaycheckStorage.calculateOvertimePay("
                        + "RobustPaycheckStorage.LEVEL_1_PAY_RATE, 46.5)");

        // Overtime Level 2 46.5 hours
        assertEquals(21937,
                RobustPaycheckStorage.calculateOvertimePay(
                        RobustPaycheckStorage.LEVEL_2_PAY_RATE, 46.5),
                "RobustPaycheckStorage.calculateOvertimePay("
                        + "RobustPaycheckStorage.LEVEL_2_PAY_RATE, 46.5)");

        // Overtime Level 3 46 hours
        assertEquals(

                25103,
                RobustPaycheckStorage.calculateOvertimePay(
                        RobustPaycheckStorage.LEVEL_3_PAY_RATE, 46.5),
                "RobustPaycheckStorage.calculateOvertimePay("
                        + "RobustPaycheckStorage.LEVEL_3_PAY_RATE, 46.5)");

        // Test a payrate other than a given constant Regular
        assertEquals(14964,
                RobustPaycheckStorage.calculateOvertimePay(1376, 47.25),
                "RobustPaycheckStorage.calculateOvertimePay(1376, 47.25)");

        // Test negative hours
        Exception exception = assertThrows(IllegalArgumentException.class,
            () -> RobustPaycheckStorage.calculateOvertimePay(
                        RobustPaycheckStorage.LEVEL_3_PAY_RATE, -1),
                "Testing negative hours worked");
        assertEquals("Negative pay rate and/or hours worked",
                exception.getMessage(),
                "Testing negative hours worked - exception message");

        // Test negative pay rate
        exception = assertThrows(IllegalArgumentException.class,
            () -> RobustPaycheckStorage.calculateOvertimePay(-20, 20),
                "Testing negative pay rate");
        assertEquals("Negative pay rate and/or hours worked",
                exception.getMessage(),
                "Testing negative pay rate - exception message");
    }

    /**
     * Test the RobustPaycheckStorage.calculateGrossPay() method.
     */
    @Test
    public void testCalculateGrossPay() {

        // Less than 40 hours
        // Gross Level 1 36 hours
        assertEquals(68400, RobustPaycheckStorage.calculateGrossPay(68400, 0),
                "RobustPaycheckStorage.calculateGrossPay(68400, 0)");

        // Gross Level 2 36 hours
        assertEquals(81000, RobustPaycheckStorage.calculateGrossPay(81000, 0),
                "RobustPaycheckStorage.calculateGrossPay(81000, 0)");

        // Gross Level 3 36 hours
        assertEquals(92700, RobustPaycheckStorage.calculateGrossPay(92700, 0),
                "RobustPaycheckStorage.calculateGrossPay(92700, 0)");

        // Over 40 hours
        // Gross Level 1 46 hours
        assertEquals(93100,
                RobustPaycheckStorage.calculateGrossPay(76000, 17100),
                "RobustPaycheckStorage.calculateGrossPay(76000, 17100)");

        // Gross Level 2 46 hours
        assertEquals(110250,
                RobustPaycheckStorage.calculateGrossPay(90000, 20250),
                "RobustPaycheckStorage.calculateGrossPay(90000, 20250)");

        // Gross Level 3 46 hours
        assertEquals(126172,
                RobustPaycheckStorage.calculateGrossPay(103000, 23172),
                "RobustPaycheckStorage.calculateGrossPay(103000, 23172)");

        // Fractional hours less than 40
        // Gross Level 1 36.5 hours
        assertEquals(69350, RobustPaycheckStorage.calculateGrossPay(69350, 0),
                "RobustPaycheckStorage.calculateGrossPay(69350, 0)");

        // Overtime Level 2 36.5 hours
        assertEquals(82125, RobustPaycheckStorage.calculateGrossPay(82125, 0),
                "RobustPaycheckStorage.calculateGrossPay(82125, 0)");

        // Gross Level 3 36.5 hours
        assertEquals(93987, RobustPaycheckStorage.calculateGrossPay(93987, 0),
                "RobustPaycheckStorage.calculateGrossPay(93987, 0)");

        // Fractional hours over 40 hours
        // Gross Level 1 46.5 hours
        assertEquals(94525,
                RobustPaycheckStorage.calculateGrossPay(76000, 18525),
                "RobustPaycheckStorage.calculateGrossPay(76000, 18525)");

        // Gross Level 2 46.5 hours
        assertEquals(111937,
                RobustPaycheckStorage.calculateGrossPay(90000, 21937),
                "RobustPaycheckStorage.calculateGrossPay(90000, 21937)");

        // Gross Level 3 46 hours
        assertEquals(128103,
                RobustPaycheckStorage.calculateGrossPay(103000, 25103),
                "RobustPaycheckStorage.calculateGrossPay(103000, 25103)");

        // Test a payrate other than a given constant Gross
        assertEquals(49914,
                RobustPaycheckStorage.calculateGrossPay(34950, 14964),
                "RobustPaycheckStorage.calculateGrossPay(34950, 14964)");

        // Test negative regular
        Exception exception = assertThrows(IllegalArgumentException.class,
            () -> RobustPaycheckStorage.calculateGrossPay(-103000, 25103),
                "Testing negative regular pay");
        assertEquals("Invalid regular and/or overtime pay.",
                exception.getMessage(),
                "Testing negative regular pay - exception message");

        // Test negative overtime
        exception = assertThrows(IllegalArgumentException.class,
            () -> RobustPaycheckStorage.calculateGrossPay(103000, -25103),
                "Testing negative overtime pay");
        assertEquals("Invalid regular and/or overtime pay.",
                exception.getMessage(),
                "Testing negative overtime pay - exception message");
    }

    /**
     * Test the RobustPaycheckStorage.calculateTotalDeductions() method.
     */
    @Test
    public void testCalculateRetirement() {

        // Zero percent retirement
        assertEquals(0, RobustPaycheckStorage.calculateRetirement(92700, 0),
                "RobustPaycheckStorage.calculateRetirement(92700, 0)");

        // One percent retirement
        assertEquals(927, RobustPaycheckStorage.calculateRetirement(92700, 1),
                "RobustPaycheckStorage.calculateRetirement(92700, 1)");

        // Two percent retirement
        assertEquals(1854, RobustPaycheckStorage.calculateRetirement(92700, 2),
                "RobustPaycheckStorage.calculateRetirement(92700, 2)");

        // Three percent retirement
        assertEquals(2781, RobustPaycheckStorage.calculateRetirement(92700, 3),
                "RobustPaycheckStorage.calculateRetirement(92700, 3)");

        // Four percent retirement
        assertEquals(3708, RobustPaycheckStorage.calculateRetirement(92700, 4),
                "RobustPaycheckStorage.calculateRetirement(92700, 4)");

        // Five percent retirement
        assertEquals(4635, RobustPaycheckStorage.calculateRetirement(92700, 5),
                "RobustPaycheckStorage.calculateRetirement(92700, 5)");

        // Six percent retirement
        assertEquals(5562, RobustPaycheckStorage.calculateRetirement(92700, 6),
                "RobustPaycheckStorage.calculateRetirement(92700, 6)");

        // Test negative gross pay
        Exception exception = assertThrows(IllegalArgumentException.class,
            () -> RobustPaycheckStorage.calculateRetirement(-92700, 6),
                "Testing negative gross pay");
        assertEquals("Invalid gross pay and/or retirement percentage.",
                exception.getMessage(),
                "Testing negative gross pay - exception message");

        // Test invalid retirement percentage
        exception = assertThrows(IllegalArgumentException.class,
            () -> RobustPaycheckStorage.calculateRetirement(92700, 7),
                "Testing invalid retirement percentage");
        assertEquals("Invalid gross pay and/or retirement percentage.",
                exception.getMessage(),
                "Testing invalid retirement percentage - exception message");
        exception = assertThrows(IllegalArgumentException.class,
            () -> RobustPaycheckStorage.calculateRetirement(92700, -1),
                "Testing invalid retirement percentage");
        assertEquals("Invalid gross pay and/or retirement percentage.",
                exception.getMessage(),
                "Testing invalid retirement percentage - exception message");
    }

    /**
     * Test the RobustPaycheckStorage.calculateNetPay() method.
     */
    @Test
    public void testCalculateNetPay() {

        // Calculate net pay with deductions
        assertEquals(86211, RobustPaycheckStorage.calculateNetPay(92700, 6489),
                "RobustPaycheckStorage.calculateNetPay(92700, 6489)");

        // Calculate net pay without deductions
        assertEquals(92700, RobustPaycheckStorage.calculateNetPay(92700, 0),
                "RobustPaycheckStorage.calculateNetPay(92700, 0)");

        // Calculate net pay - negative
        assertEquals(-1100, RobustPaycheckStorage.calculateNetPay(92700, 93800),
                "RobustPaycheckStorage.calculateNetPay(92700, 93800)");
    }

    /**
     * Test the RobustPaycheckStorage.selectInsurance() method.
     */
    @Test
    public void testSelectInsurance() {
        // Test Y
        Scanner input = new Scanner("Y\n");
        assertTrue(RobustPaycheckStorage.selectInsurance("M", input),
                "Test Y for insurance");
        // Test y
        input = new Scanner("y\n");
        assertTrue(RobustPaycheckStorage.selectInsurance("M", input),
                "Test y for insurance");

        // Test N
        input = new Scanner("N\n");
        assertFalse(RobustPaycheckStorage.selectInsurance("M", input),
                "Test N for insurance");
        // Test n
        input = new Scanner("n\n");
        assertFalse(RobustPaycheckStorage.selectInsurance("M", input),
                "Test n for insurance");

        // Test Yes
        input = new Scanner("Yes\n");
        assertTrue(RobustPaycheckStorage.selectInsurance("M", input),
                "Test Y for insurance");

        // Test never
        input = new Scanner("never\n");
        assertFalse(RobustPaycheckStorage.selectInsurance("M", input),
                "Test n for insurance");

        // Test Yes after invalid
        input = new Scanner("whatever never\n Yes  \n");
        assertTrue(RobustPaycheckStorage.selectInsurance("M", input),
                "Test Y for insurance");

        // Test never
        input = new Scanner("wolfpack, yay! \n never\n");
        assertFalse(RobustPaycheckStorage.selectInsurance("M", input),
                "Test n for insurance");
    }

    /**
     * Test the RobustPaycheckStorage.getTotalDeductions() method.
     */
    @Test
    public void testGetTotalDeductions() {
        // Test no insurance
        Scanner input = new Scanner("N\n never\n nc");
        assertEquals(0, RobustPaycheckStorage.getTotalDeductions(input));
        // Test all insurances
        input = new Scanner("Y\n yes\n yellow");
        assertEquals(4505, RobustPaycheckStorage.getTotalDeductions(input));
        // Test one insurance
        input = new Scanner("N\n yell\n nc");
        assertEquals(1530, RobustPaycheckStorage.getTotalDeductions(input));
    }

    /**
     * Test the RobustPaycheckStorage.getName() method.
     */
    @Test
    public void testGetName() {
        // Test valid name with multiple tokens
        Scanner input = new Scanner("\t Jessica Young Schmidt  \n");
        assertEquals("Jessica Young Schmidt",
                RobustPaycheckStorage.getName(input),
                "Test valid name with multiple tokens");

        // Test valid name with single token
        input = new Scanner("  Wolfpack  \n");
        assertEquals("Wolfpack", RobustPaycheckStorage.getName(input),
                "Test valid name with single token");

        // Test whitespace line before valid name with multiple tokens
        input = new Scanner("      \t \n\t Jessica Young Schmidt  \n");
        assertEquals("Jessica Young Schmidt",
                RobustPaycheckStorage.getName(input),
                "Test whitespace line before valid name with multiple tokens");

        // Test whitespace line before valid name with multiple tokens
        input = new Scanner("\n\t Jessica Young Schmidt  \n");
        assertEquals("Jessica Young Schmidt",
                RobustPaycheckStorage.getName(input),
                "Test whitespace line before valid name with multiple tokens");
    }

    /**
     * Test the RobustPaycheckStorage.getLevel() method.
     */
    @Test
    public void testGetLevel() {
        // Test valid value
        Scanner input = new Scanner("1\n");
        assertEquals(1, RobustPaycheckStorage.getLevel(input),
                "Test valid value of 1");

        // Test valid value
        input = new Scanner("2\n");
        assertEquals(2, RobustPaycheckStorage.getLevel(input),
                "Test valid value of 2");

        // Test valid value
        input = new Scanner("3\n");
        assertEquals(3, RobustPaycheckStorage.getLevel(input),
                "Test valid value of 3");

        // Test invalid value first
        input = new Scanner("level 2\n  3\n");
        assertEquals(3, RobustPaycheckStorage.getLevel(input),
                "Test invalid value then 3");

        // Test invalid value first
        input = new Scanner("0 level\n  3\n");
        assertEquals(3, RobustPaycheckStorage.getLevel(input),
                "Test invalid value then 3");

        // Test invalid value first
        input = new Scanner("0 \n  3\n");
        assertEquals(3, RobustPaycheckStorage.getLevel(input),
                "Test invalid value then 3");

        // Test invalid value first
        input = new Scanner("4\n  3\n");
        assertEquals(3, RobustPaycheckStorage.getLevel(input),
                "Test invalid value then 3");
    }

    /**
     * Test the RobustPaycheckStorage.getHoursWorked() method.
     */
    @Test
    public void testGetHoursWorked() {
        // Test boundary value
        Scanner input = new Scanner("1\n");
        assertEquals(1, RobustPaycheckStorage.getHoursWorked(input), DELTA,
                "Test boundary value for get hours worked");

        // Test boundary value
        input = new Scanner("0.1\n");
        assertEquals(0.1, RobustPaycheckStorage.getHoursWorked(input), DELTA,
                "Test boundary value for get hours worked");

        // Test mid regular value
        input = new Scanner("20.5\n");
        assertEquals(20.5, RobustPaycheckStorage.getHoursWorked(input), DELTA,
                "Test mid regular value for get hours worked");

        // Test max regular value
        input = new Scanner("40.0\n");
        assertEquals(40, RobustPaycheckStorage.getHoursWorked(input), DELTA,
                "Test max regular value for get hours worked");

        // Test overtime value
        input = new Scanner("60\n");
        assertEquals(60, RobustPaycheckStorage.getHoursWorked(input), DELTA,
                "Test overtime value for get hours worked");

        // Test invalid first value
        input = new Scanner("0\n 60\n");
        assertEquals(60, RobustPaycheckStorage.getHoursWorked(input), DELTA,
                "Test invalid first value for get hours worked");

        // Test invalid first value
        input = new Scanner("-1\n 60\n");
        assertEquals(60, RobustPaycheckStorage.getHoursWorked(input), DELTA,
                "Test invalid first value for get hours worked");

        // Test invalid first value
        input = new Scanner("four\n 60\n");
        assertEquals(60, RobustPaycheckStorage.getHoursWorked(input), DELTA,
                "Test invalid first value for get hours worked");

        // Test invalid first value
        input = new Scanner("number 3\n 60\n");
        assertEquals(60, RobustPaycheckStorage.getHoursWorked(input), DELTA,
                "Test invalid first value for get hours worked");
    }

    /**
     * Test the RobustPaycheckStorage.createPaychecks() method.
     */
    @Test
    public void testCreatePaycheck() {
        String[][] exp1 = { { null, null } };
        String[][] act1 = RobustPaycheckStorage.createPaychecks(1);
        // Test the size of array
        assertEquals(1, act1.length, "Check length of first dimension");
        assertEquals(2, act1[0].length, "Check length of second dimension");
        // test results
        assertArrayEquals(exp1, act1, "Check that array of single paycheck created");

        String[][] exp3 = { { null, null }, { null, null }, { null, null } };
        String[][] act3 = RobustPaycheckStorage.createPaychecks(3);
        // Test the size of array
        assertEquals(3, act3.length, "Check length of first dimension");
        assertEquals(2, act3[0].length, "Check length of second dimension");
        // test results
        assertArrayEquals(exp3, act3, "Check that array of three paychecks created");

        // Test invalid parameters
        Exception exception = assertThrows(IllegalArgumentException.class,
            () -> RobustPaycheckStorage.createPaychecks(0),
                "Testing non-positive number of paychecks");
        assertEquals("Invalid number of paychecks", exception.getMessage(),
                "Testing non-positive number of paychecks message");
        exception = assertThrows(IllegalArgumentException.class,
            () -> RobustPaycheckStorage.createPaychecks(-1),
                "Testing non-positive number of paychecks");
        assertEquals("Invalid number of paychecks", exception.getMessage(),
                "Testing non-positive number of paychecks message");
        exception = assertThrows(IllegalArgumentException.class,
            () -> RobustPaycheckStorage.createPaychecks(-100),
                "Testing non-positive number of paychecks");
        assertEquals("Invalid number of paychecks", exception.getMessage(),
                "Testing non-positive number of paychecks message");
    }

    /**
     * Test the RobustPaycheckStorage.getPaycheck() method.
     */
    @Test
    public void testGetPaycheck() {

        // Array with single user
        String[][] single = { { "Carol Cole",
                "Carol Cole               10.00     19.00    190.00      "
                        + "0.00    190.00      0.00    190.00\n" } };

        // Test the size of array
        assertEquals(1, single.length, "Check length of first dimension");
        assertEquals(2, single[0].length, "Check length of second dimension");
        // Test returns correct paycheck
        assertEquals(
                "Carol Cole               10.00     19.00    190.00      "
                        + "0.00    190.00      0.00    190.00\n",
                RobustPaycheckStorage.getPaycheck(single, "Carol Cole"), 
                "Check that correct paycheck returned for single paycheck");

        // Test that method doesn't modify array
        String[][] singleCopy = { { "Carol Cole",
                "Carol Cole               10.00     19.00    190.00      "
                        + "0.00    190.00      0.00    190.00\n" } };
        assertArrayEquals(singleCopy, single, "Check that array not modified");

        // Test returns no user with name
        assertEquals("No such user.",
                RobustPaycheckStorage.getPaycheck(single, "Carol"), 
                "Check that message for no user.");

        // Test that method doesn't modify array
        assertArrayEquals(singleCopy, single, "Check that array not modified");

        // Array with single user
        String[][] multiple = { { "Carol Cole",
                "Carol Cole               10.00     19.00    190.00      "
                        + "0.00    190.00      0.00    190.00\n" },
            { "Carol Cole",
                "Carol Cole               11.00     19.00    209.00      "
                                + "0.00    209.00      0.00    209.00\n" } };

        // Test the size of array
        assertEquals(2, multiple.length, "Check length of first dimension");
        assertEquals(2, multiple[0].length, "Check length of second dimension");
        // Test returns correct paycheck
        assertEquals(
                "Carol Cole               10.00     19.00    190.00      "
                + "0.00    190.00      0.00    190.00\n",
                RobustPaycheckStorage.getPaycheck(multiple, "Carol Cole"), 
                "Check that correct paycheck returned for multiple paychecks");

        // Test that method doesn't modify array
        String[][] multipleCopy = { { "Carol Cole",
                "Carol Cole               10.00     19.00    190.00      "
                        + "0.00    190.00      0.00    190.00\n" },
            { "Carol Cole",
              "Carol Cole               11.00     19.00    209.00      "
                                + "0.00    209.00      0.00    209.00\n" } };
        assertArrayEquals(multipleCopy, multiple, "Check that array not modified");

        // Test invalid parameters
        Exception exception = assertThrows(IllegalArgumentException.class,
            () -> RobustPaycheckStorage.getPaycheck(null, "CSC"),
                "Testing null array");
        assertEquals("Invalid parameters", exception.getMessage(),
                "Testing null array");
        String[][] arr = { { "Cat", "Dog", "Puppy" }, { "A", "B", "C" } };
        exception = assertThrows(IllegalArgumentException.class,
            () -> RobustPaycheckStorage.getPaycheck(arr, null),
                "Testing null name");
        assertEquals("Invalid parameters", exception.getMessage(),
                "Testing null name");
    }

    /**
     * Test the RobustPaycheckStorage.processPaycheck() method.
     */
    @Test
    public void testProcessPaycheck() {
        String expected = "Error calculating net pay.  Deductions exceed gross pay.\n";
        String actual = RobustPaycheckStorage.processPaycheck("Bob Baker", 1, 1,
                0, 5000);
        assertEquals(expected, actual, "Error message");

        expected = "Carol Cole               10.00     19.00    190.00      "
                + "0.00    190.00      0.00    190.00\n";
        actual = RobustPaycheckStorage.processPaycheck("Carol Cole", 1, 10, 0,
                0);
        assertEquals(expected, actual, "Valid input");
    }

    /**
     * Test the RobustPaycheckStorage.getRetirementPercentage() method.
     */
    @Test
    public void testGetRetirementPercentage() {
        // Test boundary value
        Scanner input = new Scanner("0\n");
        assertEquals(0, RobustPaycheckStorage.getRetirementPercentage(input),
                "Test boundary value for get retirement percentage");

        // Test boundary value
        input = new Scanner("1\n");
        assertEquals(1, RobustPaycheckStorage.getRetirementPercentage(input),
                "Test boundary value for get retirement percentage");

        // Test boundary value
        input = new Scanner("5\n");
        assertEquals(5, RobustPaycheckStorage.getRetirementPercentage(input),
                "Test boundary value for get retirement percentage");

        // Test boundary value
        input = new Scanner("6\n");
        assertEquals(6, RobustPaycheckStorage.getRetirementPercentage(input),
                "Test boundary value for get retirement percentage");

        // Test mid value
        input = new Scanner("3\n");
        assertEquals(3, RobustPaycheckStorage.getRetirementPercentage(input),
                "Test mid value for get retirement percentage");

        // Test with invalid value before valid
        input = new Scanner("words 1\n 7 \n 3 \n");
        assertEquals(3, RobustPaycheckStorage.getRetirementPercentage(input),
                "Test mid value for get retirement percentage");

        // Test with invalid value before valid
        input = new Scanner("words 1\n 3 \n");
        assertEquals(3, RobustPaycheckStorage.getRetirementPercentage(input),
                "Test mid value for get retirement percentage");
    }

    /**
     * Test the RobustPaycheckStorage.isValidRetirementPercentage() method.
     */
    @Test
    public void testIsValidRetirementPercentage() {
        // Test 0%
        assertTrue(RobustPaycheckStorage.isValidRetirementPercentage(0),
                "0% Retirement");

        // Test 1%
        assertTrue(RobustPaycheckStorage.isValidRetirementPercentage(0),
                "1% Retirement");

        // Test 3%
        assertTrue(RobustPaycheckStorage.isValidRetirementPercentage(3),
                "3% Retirement");

        // Test 5%
        assertTrue(RobustPaycheckStorage.isValidRetirementPercentage(0),
                "5% Retirement");

        // Test 6%
        assertTrue(RobustPaycheckStorage.isValidRetirementPercentage(6),
                "6% Retirement");

        // Test -1% - invalid lower boundary
        assertFalse(RobustPaycheckStorage.isValidRetirementPercentage(-1),
                "-1% Retirement");

        // Test 7% - invalid upper boundary
        assertFalse(RobustPaycheckStorage.isValidRetirementPercentage(7),
                "7% Retirement");
    }
}
