Skip to content

Assignment: Moon Mission CLI#24

Open
alicewersen-rgb wants to merge 3 commits intomainfrom
assignment1
Open

Assignment: Moon Mission CLI#24
alicewersen-rgb wants to merge 3 commits intomainfrom
assignment1

Conversation

@alicewersen-rgb
Copy link

@alicewersen-rgb alicewersen-rgb commented Dec 14, 2025

Title: Implement CLI application for moon mission management

Description:
Implemented the CLI logic in Main.run() according to the assignment requirements. The application:

  • Prompts for username and password, validates login.

  • Displays a menu with options to list missions, get mission details, count missions by year, create/update/delete accounts, and exit.

  • Uses database settings from APP_JDBC_URL, APP_DB_USER, and APP_DB_PASS.

Passes all provided integration tests using Testcontainers and Docker.

Summary by CodeRabbit

  • New Features

    • Added interactive login system with username and password authentication
    • Added menu-driven interface to list missions, query missions by ID, and count missions by year
    • Added account management features: create accounts, update passwords, and delete accounts
  • Documentation

    • Expanded README with setup instructions for testing, Docker usage, and development modes
  • Chores

    • Added MySQL database driver and testing dependencies

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Dec 14, 2025

Walkthrough

The pull request adds MySQL database dependencies and extensive documentation, then restructures the Main Java application to implement an interactive login system with a menu-driven interface for querying missions and managing user accounts.

Changes

Cohort / File(s) Summary
Documentation
README.md
Comprehensive documentation additions including test and Docker usage instructions, dev mode setup, IntelliJ configuration guidance, and feature descriptions in Swedish.
Dependencies
pom.xml
Added MySQL connector (version 9.5.0, runtime scope) and TestContainers MySQL module (version 1.21.3) for database integration testing.
Core Application Logic
src/main/java/com/example/Main.java
Made main method public; implemented interactive login flow with username/password validation against database; added menu-driven operations (list missions, query by ID, count by year, account management); introduced six new private helper methods for database operations.

Sequence Diagram

sequenceDiagram
    participant User
    participant Main as Main Application
    participant DB as Database
    
    User->>Main: Start application
    Main->>Main: Check DevMode
    Main->>DB: Load DB configuration
    Main->>Main: Initialize connection
    
    User->>Main: Input username/password
    Main->>DB: Query account table
    DB-->>Main: Account data
    alt Login Valid
        Main->>User: Login success
        User->>Main: Select menu option
        Main->>DB: Execute query<br/>(missions, counts, etc.)
        DB-->>Main: Query results
        Main->>User: Display results
    else Login Invalid
        Main->>User: Login failed
        Main->>Main: Exit
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Main.java requires careful review of the login flow, SQL query logic, and error handling across six new methods
  • Dependency versions (MySQL 9.5.0, TestContainers 1.21.3) should be verified for compatibility
  • Database connection management and credential validation logic warrant attention to security practices

Poem

🐰 A bunny hops through databases new,
With logins, menus, and SQL too!
From docs to MySQL, the warren's complete,
Now users can query each mission so neat. 🌕

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main objective of the pull request: implementing a Moon Mission CLI application with login, account management, and mission querying features.
Docstring Coverage ✅ Passed Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (1)
src/main/java/com/example/Main.java (1)

91-94: Consider more specific error handling.

Wrapping all SQLExceptions in RuntimeException loses error context. For a CLI application, providing more specific error messages to users would improve the experience.

Consider handling different SQL error scenarios:

     } catch (SQLException e) {
-        // Om något skulle gå fel med databasen.
-        throw new RuntimeException(e);
+        // Om något skulle gå fel med databasen.
+        System.err.println("Database error: " + e.getMessage());
+        if (e.getSQLState() != null) {
+            System.err.println("SQL State: " + e.getSQLState());
+        }
+        throw new RuntimeException("Database operation failed", e);
     }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e629e94 and 819a2e5.

📒 Files selected for processing (3)
  • README.md (2 hunks)
  • pom.xml (2 hunks)
  • src/main/java/com/example/Main.java (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/main/java/com/example/Main.java (1)
src/main/java/com/example/DevDatabaseInitializer.java (1)
  • DevDatabaseInitializer (6-24)
🪛 LanguageTool
README.md

[grammar] ~144-~144: Ensure spelling is correct
Context: ...r - Java 25 - JDBC - MySQL - Maven ## Testning I detta projekt används Maven för att kö...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🔇 Additional comments (8)
src/main/java/com/example/Main.java (8)

13-20: LGTM!

The main method correctly handles dev mode initialization and delegates to the run method.


22-33: LGTM!

Configuration resolution and validation are handled correctly with clear error messaging.


98-106: LGTM!

The method correctly uses try-with-resources and prepared statements.


109-126: LGTM!

The method correctly uses parameterized queries and handles the case when a mission is not found.


129-141: LGTM!

The method correctly uses parameterized queries and MySQL's YEAR() function.


181-190: LGTM!

The method correctly uses parameterized queries and provides appropriate feedback.


193-197: LGTM!

The dev mode detection correctly checks multiple sources as documented in the README.


200-206: LGTM!

Configuration resolution correctly prioritizes system properties over environment variables and handles edge cases.

Comment on lines 51 to 55
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>mysql</artifactId>
<version>1.21.3</version>
</dependency>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, let's examine the pom.xml file to confirm the dependency scope
cat pom.xml | head -60

Repository: ithsjava25/ithsjava25-java-databas-jdbc-javafx-classrom

Length of output: 2220


🏁 Script executed:

# Search for DevDatabaseInitializer to understand its usage
find . -name "DevDatabaseInitializer.java" -type f

Repository: ithsjava25/ithsjava25-java-databas-jdbc-javafx-classrom

Length of output: 154


🏁 Script executed:

# Check all testcontainers dependencies in pom.xml
rg "testcontainers" pom.xml -A 2

Repository: ithsjava25/ithsjava25-java-databas-jdbc-javafx-classrom

Length of output: 371


🏁 Script executed:

# Examine DevDatabaseInitializer.java to understand the usage context
cat ./src/main/java/com/example/DevDatabaseInitializer.java

Repository: ithsjava25/ithsjava25-java-databas-jdbc-javafx-classrom

Length of output: 870


🏁 Script executed:

# Search for usages of DevDatabaseInitializer
rg "DevDatabaseInitializer" --type java

Repository: ithsjava25/ithsjava25-java-databas-jdbc-javafx-classrom

Length of output: 270


🏁 Script executed:

# Also search for calls to .start() on DevDatabaseInitializer
rg "DevDatabaseInitializer\.start\(\)" --type java

Repository: ithsjava25/ithsjava25-java-databas-jdbc-javafx-classrom

Length of output: 178


🏁 Script executed:

# Check if there's a main method or entry point that calls this
find ./src/main/java -name "*.java" -type f | xargs grep -l "public static void main"

Repository: ithsjava25/ithsjava25-java-databas-jdbc-javafx-classrom

Length of output: 136


🏁 Script executed:

# Examine Main.java to see the context of DevDatabaseInitializer.start()
cat ./src/main/java/com/example/Main.java

Repository: ithsjava25/ithsjava25-java-databas-jdbc-javafx-classrom

Length of output: 8825


Move DevDatabaseInitializer to test source or use a Maven profile for dev-mode dependencies.

The testcontainers-mysql dependency correctly remains in compile scope since DevDatabaseInitializer is in src/main/java and must reference MySQLContainer at compile time. However, since dev mode initialization is optional (controlled via isDevMode() check), the dependency is unnecessarily bundled in production artifacts.

Current state: Dev mode is already conditional and requires explicit activation (system property devMode, environment variable DEV_MODE, or --dev argument), so the class is never instantiated in standard production runs.

Recommended solution: Move src/main/java/com/example/DevDatabaseInitializer.java to src/test/java and add <scope>test</scope> to the testcontainers-mysql dependency. This aligns the code organization with its actual purpose as a development/testing utility and prevents the dependency from being packaged in production artifacts.

Alternatively, create a separate Maven profile for dev mode that includes the Testcontainers dependency only when explicitly activated.

🤖 Prompt for AI Agents
In pom.xml around lines 51-55, the testcontainers MySQL dependency is declared
without test scope causing it to be bundled into production artifacts because
DevDatabaseInitializer lives in src/main/java; either move
src/main/java/com/example/DevDatabaseInitializer.java to
src/test/java/com/example/ so it becomes a test-only utility and then change the
org.testcontainers:mysql dependency to include <scope>test</scope>, or instead
leave the class in main and wrap the dependency in a dedicated Maven profile
(e.g., <profile>dev</profile>) that adds the testcontainers dependency only when
the dev profile is explicitly activated; apply one of these two changes and
update any package/import paths if the class is moved.

Comment on lines 36 to +60
try (Connection connection = DriverManager.getConnection(jdbcUrl, dbUser, dbPass)) {
// För att läsa input av användaren i terminalen, så används Scanner.
Scanner scanner = new Scanner(System.in);

// Loggar in med användarnamn och lösenord.
System.out.print("Username: ");
String username = scanner.nextLine();
System.out.print("Password: ");
String password = scanner.nextLine();

// En SQL-fråga för att kontrollera användarnamn och lösenord.
String loginSql = "SELECT * FROM account WHERE name = ? AND password = ?";
try (PreparedStatement stmt = connection.prepareStatement(loginSql)) {
stmt.setString(1, username);
stmt.setString(2, password);
try (ResultSet rs = stmt.executeQuery()) {
// Om ingen rad hittas, så misslyckas inloggningen.
if (!rs.next()) {
System.out.println("Invalid username or password");
return; // Avslutar run() om inloggningen misslyckas.
}
}
}
// Fortsätter programmet efter lyckad inloggning.
System.out.println("Login successful!");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Resource leak: Scanner is never closed.

The Scanner created at line 38 is never closed, which can lead to resource leaks. Since the scanner wraps System.in, closing it would close the underlying stream, but the scanner should still be managed properly.

Consider restructuring to ensure proper resource management:

     try (Connection connection = DriverManager.getConnection(jdbcUrl, dbUser, dbPass)) {
-        // För att läsa input av användaren i terminalen, så används Scanner.
-        Scanner scanner = new Scanner(System.in);
+        try (Scanner scanner = new Scanner(System.in)) {
+            // För att läsa input av användaren i terminalen, så används Scanner.

-    } catch (SQLException e) {
+        }
+    } catch (SQLException e) {

Note: Be aware that closing a Scanner that wraps System.in will also close System.in, making it unavailable for subsequent reads. If this is a concern, document this behavior or consider alternative input handling approaches.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/main/java/com/example/Main.java around lines 36 to 60, the Scanner
instantiated for System.in is never closed which risks a resource leak; either
wrap the Scanner in a try-with-resources to ensure it is closed (accepting that
this will close System.in) or, if you must keep System.in open for later use,
reuse a single shared Scanner (e.g., a static or method-scoped Scanner) and
document that it intentionally remains open; update the code to follow one of
these patterns and add a brief comment explaining the choice.

Comment on lines +47 to +58
String loginSql = "SELECT * FROM account WHERE name = ? AND password = ?";
try (PreparedStatement stmt = connection.prepareStatement(loginSql)) {
stmt.setString(1, username);
stmt.setString(2, password);
try (ResultSet rs = stmt.executeQuery()) {
// Om ingen rad hittas, så misslyckas inloggningen.
if (!rs.next()) {
System.out.println("Invalid username or password");
return; // Avslutar run() om inloggningen misslyckas.
}
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check for password hashing implementation

echo "Checking if init.sql contains hashed passwords..."
if [ -f "src/main/resources/init.sql" ]; then
    grep -i "password" src/main/resources/init.sql | head -20
fi

echo ""
echo "Searching for password hashing libraries or utilities in the codebase..."
rg -n "hash|bcrypt|pbkdf2|scrypt|argon" --type java -g '!**/test/**'

Repository: ithsjava25/ithsjava25-java-databas-jdbc-javafx-classrom

Length of output: 487


🏁 Script executed:

#!/bin/bash
# Find the createAccount method
echo "Searching for createAccount method..."
rg -n "createAccount|CREATE.*ACCOUNT" --type java -A 15

echo ""
echo "Checking for any password manipulation in Java code..."
rg -n "password" --type java -B 2 -A 2 | head -60

Repository: ithsjava25/ithsjava25-java-databas-jdbc-javafx-classrom

Length of output: 9140


Passwords must be hashed using a secure algorithm.

Passwords are currently stored and validated as plain text in the database. Lines 47–58 compare the input password directly against the password column without any hashing. The createAccount method (lines 144–159) also stores passwords in plain text. Use a secure hashing algorithm such as bcrypt, PBKDF2, Argon2, or scrypt for both storage and validation. Apply hashing in createAccount when inserting and modify the login query to validate against the hashed value.

Comment on lines +144 to +163
private void createAccount(Connection conn, Scanner scanner) throws SQLException {
System.out.print("First name: ");
String firstName = scanner.nextLine();
System.out.print("Last name: ");
String lastName = scanner.nextLine();
System.out.print("SSN: ");
String ssn = scanner.nextLine();
System.out.print("Password: ");
String password = scanner.nextLine();

try (PreparedStatement stmt = conn.prepareStatement(
"INSERT INTO account (first_name, last_name, ssn, password) VALUES (?, ?, ?, ?)")) {
stmt.setString(1, firstName);
stmt.setString(2, lastName);
stmt.setString(3, ssn);
stmt.setString(4, password);
stmt.executeUpdate();
System.out.println("Account created!");
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add input validation and improve error handling.

The method has several concerns:

  1. No input validation: Empty strings or invalid SSN formats are not checked before database insertion
  2. Generic error handling: SQL constraint violations (e.g., duplicate SSN) are caught by the generic handler in run(), providing poor user feedback
  3. Password storage: Passwords appear to be stored without hashing (related to earlier comment)

Consider adding validation:

 private void createAccount(Connection conn, Scanner scanner) throws SQLException {
     System.out.print("First name: ");
     String firstName = scanner.nextLine();
+    if (firstName == null || firstName.trim().isEmpty()) {
+        System.out.println("First name cannot be empty.");
+        return;
+    }
+    
     System.out.print("Last name: ");
     String lastName = scanner.nextLine();
+    if (lastName == null || lastName.trim().isEmpty()) {
+        System.out.println("Last name cannot be empty.");
+        return;
+    }
+    
     System.out.print("SSN: ");
     String ssn = scanner.nextLine();
+    if (ssn == null || ssn.trim().isEmpty()) {
+        System.out.println("SSN cannot be empty.");
+        return;
+    }
+    
     System.out.print("Password: ");
     String password = scanner.nextLine();
+    if (password == null || password.trim().isEmpty()) {
+        System.out.println("Password cannot be empty.");
+        return;
+    }
+    // TODO: Hash password before storage

     try (PreparedStatement stmt = conn.prepareStatement(
             "INSERT INTO account (first_name, last_name, ssn, password) VALUES (?, ?, ?, ?)")) {
         stmt.setString(1, firstName);
         stmt.setString(2, lastName);
         stmt.setString(3, ssn);
         stmt.setString(4, password);
         stmt.executeUpdate();
         System.out.println("Account created!");
+    } catch (SQLException e) {
+        if (e.getSQLState() != null && e.getSQLState().startsWith("23")) {
+            System.out.println("Failed to create account: Duplicate SSN or constraint violation.");
+        } else {
+            throw e;
+        }
     }
 }

Comment on lines +166 to +178
private void updatePassword(Connection conn, Scanner scanner) throws SQLException {
System.out.print("User ID: ");
String userId = scanner.nextLine();
System.out.print("New password: ");
String newPassword = scanner.nextLine();

try (PreparedStatement stmt = conn.prepareStatement("UPDATE account SET password = ? WHERE user_id = ?")) {
stmt.setString(1, newPassword);
stmt.setString(2, userId);
int rows = stmt.executeUpdate();
System.out.println(rows > 0 ? "Password updated!" : "User not found.");
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add validation for empty password.

The method doesn't validate that the new password is non-empty before updating the database.

Apply this diff to add validation:

 private void updatePassword(Connection conn, Scanner scanner) throws SQLException {
     System.out.print("User ID: ");
     String userId = scanner.nextLine();
     System.out.print("New password: ");
     String newPassword = scanner.nextLine();
+    
+    if (newPassword == null || newPassword.trim().isEmpty()) {
+        System.out.println("Password cannot be empty.");
+        return;
+    }
+    // TODO: Hash password before storage

     try (PreparedStatement stmt = conn.prepareStatement("UPDATE account SET password = ? WHERE user_id = ?")) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private void updatePassword(Connection conn, Scanner scanner) throws SQLException {
System.out.print("User ID: ");
String userId = scanner.nextLine();
System.out.print("New password: ");
String newPassword = scanner.nextLine();
try (PreparedStatement stmt = conn.prepareStatement("UPDATE account SET password = ? WHERE user_id = ?")) {
stmt.setString(1, newPassword);
stmt.setString(2, userId);
int rows = stmt.executeUpdate();
System.out.println(rows > 0 ? "Password updated!" : "User not found.");
}
}
private void updatePassword(Connection conn, Scanner scanner) throws SQLException {
System.out.print("User ID: ");
String userId = scanner.nextLine();
System.out.print("New password: ");
String newPassword = scanner.nextLine();
if (newPassword == null || newPassword.trim().isEmpty()) {
System.out.println("Password cannot be empty.");
return;
}
// TODO: Hash password before storage
try (PreparedStatement stmt = conn.prepareStatement("UPDATE account SET password = ? WHERE user_id = ?")) {
stmt.setString(1, newPassword);
stmt.setString(2, userId);
int rows = stmt.executeUpdate();
System.out.println(rows > 0 ? "Password updated!" : "User not found.");
}
}
🤖 Prompt for AI Agents
In src/main/java/com/example/Main.java around lines 166 to 178, the
updatePassword method lacks validation for an empty newPassword; before
preparing/executing the UPDATE, check that newPassword is not null and not blank
(e.g., trim and verify length > 0), print an error message and return early
without executing the SQL when validation fails, and only proceed to create the
PreparedStatement and run executeUpdate when the newPassword passes validation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant