diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml index 675fde4..f3da700 100644 --- a/.idea/inspectionProfiles/Project_Default.xml +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -2,5 +2,26 @@ <profile version="1.0"> <option name="myName" value="Project Default" /> <inspection_tool class="UseOfJDBCDriverClass" enabled="true" level="WARNING" enabled_by_default="true" /> + <inspection_tool class="VulnerableLibrariesLocal" enabled="true" level="WARNING" enabled_by_default="true"> + <option name="isIgnoringEnabled" value="true" /> + <option name="ignoredModules"> + <list> + <option value="loc-counting" /> + <option value="loc-counting" /> + </list> + </option> + <option name="ignoredPackages"> + <list> + <option value="com.google.guava:guava:30.1.1-jre" /> + <option value="org.yaml:snakeyaml:1.23" /> + </list> + </option> + <option name="ignoredReasons"> + <list> + <option value="Not exploitable" /> + <option value="Not exploitable" /> + </list> + </option> + </inspection_tool> </profile> </component> \ No newline at end of file diff --git a/README.md b/README.md index 77825cf..94702b5 100644 --- a/README.md +++ b/README.md @@ -5,11 +5,20 @@ - [Technologies Used](#technologies-used) - [Implementation Details](#implementation-details) - [Known Bugs](#known-bugs) +- [Setup and Installation](#setup-and-installation) - [About the Team](#about-the-team) - [Contact Information](#contact-information) ## About the App -The SoftEng Health Club is known for its commitment to member satisfaction. It aims to enhance its services with a new membership management system. As the club grows, it seeks to streamline operations and improve communication with its members. This system will help SoftEng efficiently manage memberships, handle renewals, and support better member engagement. With a focus on maintaining its reputation for excellent service. This new system will ensure both staff and members enjoy a smooth experience. The system will be designed to handle various membership durations, notify members of upcoming renewals, and allow for on-the-spot membership renewals and sign-up. Additionally, it will offer tools for the club’s management to monitor member activity and generate reports for operational decisions. The following document outlines the complete design needed to meet these objectives and ensure smooth operation for both members and staff. The SoftEng Health Club's suggested membership management system is made to specifically address the demands of both the club and its customers. This solution will increase membership satisfaction by emphasizing automation, user experience, and data insights through specialized latent tasks. +The SoftEng Health Club is known for its commitment to member satisfaction. It aims to enhance its services with a new membership management system. As the club grows, it seeks to streamline operations and improve communication with its members. This system will help SoftEng efficiently manage memberships, handle renewals, and support better member engagement. With a focus on maintaining its reputation for excellent service, this new system will ensure both staff and members enjoy a smooth experience. + +The system is designed to: +- Handle various membership durations. +- Notify members of upcoming renewals. +- Allow on-the-spot membership renewals and sign-ups. +- Provide tools for management to monitor member activity and generate reports for informed decision-making. + +This document outlines the complete design needed to meet these objectives, ensuring seamless operation for both members and staff. The SoftEng Health Club's membership management system is crafted to address the demands of both the club and its members, emphasizing automation, user experience, and data insights. ## Technologies Used - **Java**: Primary programming language for backend development. @@ -18,32 +27,57 @@ The SoftEng Health Club is known for its commitment to member satisfaction. It a ## Implementation Details -The SoftEng Health Club Membership Management System comprises several Java classes and interfaces, each responsible for specific functionalities that ensure robust operation and maintainability. Here's an overview of the key components: - ### Database Integration: - - Utilizes MySQL for data storage, managing members' data, session logs, and authentication details. -- Connection and query executions are managed through standard JDBC API calls, ensuring reliable data transactions and security against SQL injection via prepared statements. +- Connection and queries are handled through the JDBC API, ensuring reliable data transactions. +- Uses prepared statements to safeguard against SQL injection. ### User Interface: +- Designed using Java Swing, providing a familiar native look and feel. +- The login page and other UI components utilize a `GridLayoutManager` to ensure responsive and user-friendly design on various screen resolutions. -- Designed using Java Swing, providing a native look and feel that is familiar to users. -- The login page is created with a combination of labels, text fields, and buttons laid out using a `GridLayoutManager` to ensure a responsive layout across different screen resolutions. +## Known Bugs +- **Selection Issue After Sorting**: Selecting a member after sorting the table by a column currently selects the member previously at that row before sorting. This causes incorrect member actions. +- **Array Out of Bounds Error**: Clicking the delete button without selecting a member results in an ArrayOutOfBoundsException, potentially causing application crashes and data loss. +## Setup and Installation +1. **Install IntelliJ IDEA** + Download and install IntelliJ IDEA from [JetBrains](https://www.jetbrains.com/idea/download/). -## Known Bugs -- **Selection Issue After Sorting**: Selecting a member after sorting the table by column selects the member that was in the row before sorting. This can lead to incorrect member actions and needs attention to ensure accurate data manipulation. -- **Array Out of Bounds Error**: An array out of bounds error occurs when the delete button is clicked without selecting a member. This error can crash the application, leading to potential data loss and reduced system stability. +2. **Install JavaFX Plugin** + In IntelliJ, go to `File > Settings > Plugins` and search for "JavaFX" to install the required plugin. + +3. **Configure Source Directories** + After cloning the repository, right-click on the `src` and `res` directories in the Project pane and select: + `Mark Directory as > Source Directory` + This ensures IntelliJ recognizes them as part of the project's source structure. + +4. **Set JDK to Temurin 17** + Go to `File > Project Structure > Project` and set the "Project SDK" to `temurin-17` (or another JDK 17 distribution you have installed). ## About the Team -This project is developed by a dedicated team of software engineering students from the COMP330 course. The team has embraced the challenge of developing a robust membership management system and has shown exceptional commitment to continuous improvement and clear communication. +This project is developed by a dedicated team of software engineering students from the COMP330 course, who have demonstrated commitment to building a reliable membership management system, emphasizing continuous improvement and open communication. ## Contact Information -For more details about the project, or to reach out with questions or suggestions, please contact the following team members: -- **Nahum Gessesse** - Developer - Email: [nahumguess@gmail.com](mailto:nahumguess@gmail.com) -- **Xander Estevez** - Developer - Email: [xandereste633@gmail.com](mailto:xandereste633@gmail.com) -- **Manali Deb** - QA/Technical Writer - Email: [debook4@gmail.com](mailto:debook4@gmail.com) -- **Alina Zacaria** - Tech Lead - Email: [alinazac9@gmail.com](mailto:alinazac9@gmail.com) -- **Andrew Do** - Developer - Email: [Andrewydo@gmail.com](mailto:Andrewydo@gmail.com) -- **Nick Calhoun** - Developer - Email: [nickfilms32@gmail.com](mailto:nickfilms32@gmail.com) -- **Jai Fischer** - Developer - Email: [jaifischer@gmail.com](mailto:jaifischer@gmail.com) +For more details or inquiries, please contact: + +- **Nahum Gessesse** - Developer + Email: [nahumguess@gmail.com](mailto:nahumguess@gmail.com) + +- **Xander Estevez** - Developer + Email: [xandereste633@gmail.com](mailto:xandereste633@gmail.com) + +- **Manali Deb** - QA/Technical Writer + Email: [debook4@gmail.com](mailto:debook4@gmail.com) + +- **Alina Zacaria** - Tech Lead + Email: [alinazac9@gmail.com](mailto:alinazac9@gmail.com) + +- **Andrew Do** - Developer + Email: [Andrewydo@gmail.com](mailto:Andrewydo@gmail.com) + +- **Nick Calhoun** - Developer + Email: [nickfilms32@gmail.com](mailto:nickfilms32@gmail.com) + +- **Jai Fischer** - Developer + Email: [jaifischer@gmail.com](mailto:jaifischer@gmail.com) diff --git a/pom.xml b/pom.xml index 8cbfcd2..b2a42f0 100644 --- a/pom.xml +++ b/pom.xml @@ -26,6 +26,19 @@ <artifactId>javafaker</artifactId> <version>1.0.2</version> </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>4.13.1</version> + <scope>compile</scope> + </dependency> + + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <version>3.12.4</version> + <scope>compile</scope> + </dependency> </dependencies> <build> diff --git a/src/main/project/pages/CreateForm.java b/src/main/project/pages/CreateForm.java index 7c7b1d3..5aa3071 100644 --- a/src/main/project/pages/CreateForm.java +++ b/src/main/project/pages/CreateForm.java @@ -110,18 +110,57 @@ public int checkIfMemberExists(Member member, Connection conn) { public Long createUniqueID(Connection conn) { long id = 10000000000L; - try{ - while(true){ + try { + while (true) { String query = "SELECT * FROM members WHERE id_number=?"; PreparedStatement preparedStatement = conn.prepareStatement(query); preparedStatement.setLong(1, id); ResultSet rs = preparedStatement.executeQuery(); - if(!rs.next()) break; - else id = (long)(Math.random() * (99999999999L - 10000000000L)) + 10000000000L; + if (!rs.next()) break; + else id = (long) (Math.random() * (99999999999L - 10000000000L)) + 10000000000L; } } catch (Exception e) { e.printStackTrace(); } return id; } + + + +// Implement the getter methods + public JTextField getFirstNameField() { + return tfFirstName; + } + + public JTextField getLastNameField() { + return tfLastName; + } + + public JTextField getEmailField() { + return tfEmail; + } + + public JTextField getPhoneNumberField() { + return tfPhoneNumber; + } + + public JTextField getBdMonthField() { + return bdMonth; + } + + public JTextField getBdDayField() { + return bdDay; + } + + public JTextField getBdYearField() { + return bdYear; + } + + public JTextField getMembershipDurationField() { + return MembershipDuration; + } + + public JButton getCreateButton() { + return createButton; + } } diff --git a/src/main/project/pages/HealthClubHomePage.java b/src/main/project/pages/HealthClubHomePage.java index 2c8aefd..f4063ae 100644 --- a/src/main/project/pages/HealthClubHomePage.java +++ b/src/main/project/pages/HealthClubHomePage.java @@ -3,7 +3,6 @@ import main.project.modules.Guest; import main.project.modules.Member; import main.project.modules.Status; -import modules.*; import javax.swing.*; import javax.swing.table.DefaultTableModel; import java.awt.*; diff --git a/src/test/TestCreateForm.java b/src/test/TestCreateForm.java index e69de29..89c17b6 100644 --- a/src/test/TestCreateForm.java +++ b/src/test/TestCreateForm.java @@ -0,0 +1,141 @@ +package test; + +import com.github.javafaker.Faker; +import main.project.modules.Member; +import main.project.pages.CreateForm; +import org.junit.Before; +import org.junit.Test; +import javax.swing.*; +import java.sql.*; +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +public class TestCreateForm { + + private CreateForm createForm; + private Member memberMock; + private Connection connMock; + private Faker faker; + + private JTextField tfFirstName; + private JTextField tfLastName; + private JTextField tfEmail; + private JTextField tfPhoneNumber; + private JTextField tfBdMonth; + private JTextField tfBdDay; + private JTextField tfBdYear; + private JTextField tfMembershipDuration; + private JButton createButton; + + @Before + public void setUp() { + faker = new Faker(); + + tfFirstName = mock(JTextField.class); + tfLastName = mock(JTextField.class); + tfEmail = mock(JTextField.class); + tfPhoneNumber = mock(JTextField.class); + tfBdMonth = mock(JTextField.class); + tfBdDay = mock(JTextField.class); + tfBdYear = mock(JTextField.class); + tfMembershipDuration = mock(JTextField.class); + createButton = mock(JButton.class); + createForm = mock(CreateForm.class); + doReturn(tfFirstName).when(createForm).getFirstNameField(); + doReturn(tfLastName).when(createForm).getLastNameField(); + doReturn(tfEmail).when(createForm).getEmailField(); + doReturn(tfPhoneNumber).when(createForm).getPhoneNumberField(); + doReturn(tfBdMonth).when(createForm).getBdMonthField(); + doReturn(tfBdDay).when(createForm).getBdDayField(); + doReturn(tfBdYear).when(createForm).getBdYearField(); + doReturn(tfMembershipDuration).when(createForm).getMembershipDurationField(); + doReturn(createButton).when(createForm).getCreateButton(); + + connMock = mock(Connection.class); + } + + @Test + public void testCreateMember_Success() throws Exception { + String firstName = faker.name().firstName(); + String lastName = faker.name().lastName(); + String email = faker.internet().emailAddress(); + long phoneNumber = 4151234567L; + int membershipDuration = faker.number().numberBetween(6, 24); + + when(tfFirstName.getText()).thenReturn(firstName); + when(tfLastName.getText()).thenReturn(lastName); + when(tfEmail.getText()).thenReturn(email); + when(tfPhoneNumber.getText()).thenReturn(String.valueOf(phoneNumber)); + when(tfBdMonth.getText()).thenReturn("12"); + when(tfBdDay.getText()).thenReturn("15"); + when(tfBdYear.getText()).thenReturn("1990"); + when(tfMembershipDuration.getText()).thenReturn(String.valueOf(membershipDuration)); + + + Member member = new Member(firstName, lastName, email, faker.date().birthday().toInstant().atZone(java.time.ZoneId.systemDefault()).toLocalDate(), phoneNumber); + when(connMock.prepareStatement(anyString())).thenReturn(mock(PreparedStatement.class)); + + assertTrue(createForm.checkIfMemberExists(member, connMock) == 0); + } + + @Test + public void testCreateMember_EmailAlreadyExists() throws Exception { + String firstName = faker.name().firstName(); + String lastName = faker.name().lastName(); + String email = faker.internet().emailAddress(); + long phoneNumber = 6265552349L; + int membershipDuration = faker.number().numberBetween(6, 24); + + Member member = new Member(firstName, lastName, email, faker.date().birthday().toInstant().atZone(java.time.ZoneId.systemDefault()).toLocalDate(), phoneNumber); + when(createForm.checkIfMemberExists(any(), eq(connMock))).thenReturn(1); + + long res = createForm.createMember(member, membershipDuration); + assertEquals(0, res); + } + + @Test + public void testCreateMember_PhoneAlreadyExists() throws Exception { + String firstName = faker.name().firstName(); + String lastName = faker.name().lastName(); + String email = faker.internet().emailAddress(); + long phoneNumber = 8325678901L; + int membershipDuration = faker.number().numberBetween(6, 24); + + Member member = new Member(firstName, lastName, email, faker.date().birthday().toInstant().atZone(java.time.ZoneId.systemDefault()).toLocalDate(), phoneNumber); + when(createForm.checkIfMemberExists(any(), eq(connMock))).thenReturn(2); // 2 means phone number exists + + long res = createForm.createMember(member, membershipDuration); + assertEquals(0, res); + } + + @Test + public void testCreateMember_GenerateMetrics() throws Exception { + String firstName = faker.name().firstName(); + String lastName = faker.name().lastName(); + String email = faker.internet().emailAddress(); + long phoneNumber = 3214509876L; + int membershipDuration = faker.number().numberBetween(6, 24); + + Member member = new Member(firstName, lastName, email, faker.date().birthday().toInstant().atZone(java.time.ZoneId.systemDefault()).toLocalDate(), phoneNumber); + + long startTime = System.nanoTime(); + + when(createForm.createMember(any(), anyInt())).thenReturn(12345L); + long memberId = createForm.createMember(member, membershipDuration); + + long endTime = System.nanoTime(); + long execTime = endTime - startTime; + + Runtime runtime = Runtime.getRuntime(); + long memBefore = runtime.totalMemory() - runtime.freeMemory(); + + long memAfter = runtime.totalMemory() - runtime.freeMemory(); + long memUsage = memAfter - memBefore; + + assertEquals(12345L, memberId); + System.out.println("Execution Time: "+execTime+" ns"); + System.out.println("Memory Usage: "+memUsage+ " bytes"); + assertTrue("Execution time is too long", execTime < 1000000000L); // 1 second max + verify(createForm, times(1)).createMember(any(), anyInt()); + } +} diff --git a/target/checkstyle-cachefile b/target/checkstyle-cachefile new file mode 100644 index 0000000..7291acf --- /dev/null +++ b/target/checkstyle-cachefile @@ -0,0 +1,2 @@ +#Thu Dec 05 15:45:49 CST 2024 +configuration*?=E3A0464A86BDB5AD0445F393F4DB4760762F386E diff --git a/target/checkstyle-checker.xml b/target/checkstyle-checker.xml new file mode 100644 index 0000000..35826d1 --- /dev/null +++ b/target/checkstyle-checker.xml @@ -0,0 +1,177 @@ +<?xml version="1.0"?> +<!DOCTYPE module PUBLIC + "-//Checkstyle//DTD Checkstyle Configuration 1.3//EN" + "https://checkstyle.org/dtds/configuration_1_3.dtd"> + +<!-- + + Checkstyle configuration that checks the sun coding conventions from: + + - the Java Language Specification at + https://docs.oracle.com/javase/specs/jls/se11/html/index.html + + - the Sun Code Conventions at https://www.oracle.com/technetwork/java/codeconvtoc-136057.html + + - the Javadoc guidelines at + https://www.oracle.com/technetwork/java/javase/documentation/index-137868.html + + - the JDK Api documentation https://docs.oracle.com/en/java/javase/11/ + + - some best practices + + Checkstyle is very configurable. Be sure to read the documentation at + http://checkstyle.sourceforge.net (or in your downloaded distribution). + + Most Checks are configurable, be sure to consult the documentation. + + To completely disable a check, just comment it out or delete it from the file. + + Finally, it is worth reading the documentation. + +--> + +<module name="Checker"> + <!-- + If you set the basedir property below, then all reported file + names will be relative to the specified directory. See + https://checkstyle.org/5.x/config.html#Checker + + <property name="basedir" value="${basedir}"/> + --> + + <property name="fileExtensions" value="java, properties, xml"/> + + <!-- Excludes all 'module-info.java' files --> + <!-- See https://checkstyle.org/config_filefilters.html --> + <module name="BeforeExecutionExclusionFileFilter"> + <property name="fileNamePattern" value="module\-info\.java$"/> + </module> + + <!-- Checks that a package-info.java file exists for each package. --> + <!-- See http://checkstyle.sourceforge.net/config_javadoc.html#JavadocPackage --> + <module name="JavadocPackage"/> + + <!-- Checks whether files end with a new line. --> + <!-- See http://checkstyle.sourceforge.net/config_misc.html#NewlineAtEndOfFile --> + <module name="NewlineAtEndOfFile"/> + + <!-- Checks that property files contain the same keys. --> + <!-- See http://checkstyle.sourceforge.net/config_misc.html#Translation --> + <module name="Translation"/> + + <!-- Checks for Size Violations. --> + <!-- See http://checkstyle.sourceforge.net/config_sizes.html --> + <module name="FileLength"/> + + <!-- Checks for whitespace --> + <!-- See http://checkstyle.sourceforge.net/config_whitespace.html --> + <module name="FileTabCharacter"/> + + <!-- Miscellaneous other checks. --> + <!-- See http://checkstyle.sourceforge.net/config_misc.html --> + <module name="RegexpSingleline"> + <property name="format" value="\s+$"/> + <property name="minimum" value="0"/> + <property name="maximum" value="0"/> + <property name="message" value="Line has trailing spaces."/> + </module> + + <!-- Checks for Headers --> + <!-- See http://checkstyle.sourceforge.net/config_header.html --> + <!-- <module name="Header"> --> + <!-- <property name="headerFile" value="${checkstyle.header.file}"/> --> + <!-- <property name="fileExtensions" value="java"/> --> + <!-- </module> --> + + <module name="TreeWalker"> + + <!-- Checks for Javadoc comments. --> + <!-- See http://checkstyle.sourceforge.net/config_javadoc.html --> + <module name="JavadocMethod"/> + <module name="JavadocType"/> + <module name="JavadocVariable"/> + <module name="JavadocStyle"/> + + <!-- Checks for Naming Conventions. --> + <!-- See http://checkstyle.sourceforge.net/config_naming.html --> + <module name="ConstantName"/> + <module name="LocalFinalVariableName"/> + <module name="LocalVariableName"/> + <module name="MemberName"/> + <module name="MethodName"/> + <module name="PackageName"/> + <module name="ParameterName"/> + <module name="StaticVariableName"/> + <module name="TypeName"/> + + <!-- Checks for imports --> + <!-- See http://checkstyle.sourceforge.net/config_import.html --> + <module name="AvoidStarImport"/> + <module name="IllegalImport"/> <!-- defaults to sun.* packages --> + <module name="RedundantImport"/> + <module name="UnusedImports"> + <property name="processJavadoc" value="false"/> + </module> + + <!-- Checks for Size Violations. --> + <!-- See http://checkstyle.sourceforge.net/config_sizes.html --> + <module name="LineLength"/> + <module name="MethodLength"/> + <module name="ParameterNumber"/> + + <!-- Checks for whitespace --> + <!-- See http://checkstyle.sourceforge.net/config_whitespace.html --> + <module name="EmptyForIteratorPad"/> + <module name="GenericWhitespace"/> + <module name="MethodParamPad"/> + <module name="NoWhitespaceAfter"/> + <module name="NoWhitespaceBefore"/> + <module name="OperatorWrap"/> + <module name="ParenPad"/> + <module name="TypecastParenPad"/> + <module name="WhitespaceAfter"/> + <module name="WhitespaceAround"/> + + <!-- Modifier Checks --> + <!-- See http://checkstyle.sourceforge.net/config_modifiers.html --> + <module name="ModifierOrder"/> + <module name="RedundantModifier"/> + + <!-- Checks for blocks. You know, those {}'s --> + <!-- See http://checkstyle.sourceforge.net/config_blocks.html --> + <module name="AvoidNestedBlocks"/> + <module name="EmptyBlock"/> + <module name="LeftCurly"/> + <module name="NeedBraces"/> + <module name="RightCurly"/> + + <!-- Checks for common coding problems --> + <!-- See http://checkstyle.sourceforge.net/config_coding.html --> + <module name="EmptyStatement"/> + <module name="EqualsHashCode"/> + <module name="HiddenField"/> + <module name="IllegalInstantiation"/> + <module name="InnerAssignment"/> + <module name="MagicNumber"/> + <module name="MissingSwitchDefault"/> + <module name="SimplifyBooleanExpression"/> + <module name="SimplifyBooleanReturn"/> + + <!-- Checks for class design --> + <!-- See http://checkstyle.sourceforge.net/config_design.html --> + <module name="DesignForExtension"/> + <module name="FinalClass"/> + <module name="HideUtilityClassConstructor"/> + <module name="InterfaceIsType"/> + <module name="VisibilityModifier"/> + + <!-- Miscellaneous other checks. --> + <!-- See http://checkstyle.sourceforge.net/config_misc.html --> + <module name="ArrayTypeStyle"/> + <module name="FinalParameters"/> + <module name="TodoComment"/> + <module name="UpperEll"/> + + </module> + +</module> diff --git a/target/checkstyle-result.xml b/target/checkstyle-result.xml new file mode 100644 index 0000000..7e142e4 --- /dev/null +++ b/target/checkstyle-result.xml @@ -0,0 +1,3 @@ +<?xml version="1.0" encoding="UTF-8"?> +<checkstyle version="8.19"> +</checkstyle> diff --git a/target/maven-archiver/pom.properties b/target/maven-archiver/pom.properties new file mode 100644 index 0000000..e8b507f --- /dev/null +++ b/target/maven-archiver/pom.properties @@ -0,0 +1,3 @@ +artifactId=loc-counting +groupId=com.example +version=1.0-SNAPSHOT