diff --git a/account-service/src/test/java/com/drinkool/entities/CustomerEntityTest.java b/account-service/src/test/java/com/drinkool/entities/CustomerEntityTest.java new file mode 100644 index 0000000..a21454a --- /dev/null +++ b/account-service/src/test/java/com/drinkool/entities/CustomerEntityTest.java @@ -0,0 +1,56 @@ +package com.drinkool.entities; + +import io.quarkus.test.junit.QuarkusTest; +import jakarta.inject.Inject; +import jakarta.persistence.EntityManager; +import jakarta.transaction.Transactional; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +@QuarkusTest +public class CustomerEntityTest { + + @Inject + EntityManager entityManager; + + @Test + @Transactional + public void testCustomerPersistence() { + // 1. Khởi tạo đúng Entity (CustomerEntity chứ không phải CustomerEntityTest) + CustomerEntity customer = new CustomerEntity(); + + // Gán đúng biến name, phone và hashedPassword (các trường kế thừa từ UserModel) + customer.name = "Nguyen Van A"; + customer.phone = "+84987654321"; + customer.hashedPassword = "dummy_hashed_password"; + + // 2. Lưu vào DB + entityManager.persist(customer); + entityManager.flush(); + entityManager.clear(); + + // 3. Truy vấn lại để kiểm tra (Sửa lại class map của query thành CustomerEntity) + var query = entityManager.createQuery( + "SELECT c FROM CustomerEntity c WHERE c.phone = :phone", + CustomerEntity.class + ); + query.setParameter("phone", "+84987654321"); + + // Nhận kết quả về đúng kiểu CustomerEntity + CustomerEntity savedCustomer = query.getSingleResult(); + + // 4. Assert (Kiểm chứng) + Assertions.assertNotNull(savedCustomer, "Customer không được null"); + Assertions.assertEquals( + "Nguyen Van A", + savedCustomer.name, + "Tên không khớp" + ); + Assertions.assertEquals( + "+84987654321", + savedCustomer.phone, + "Số điện thoại không khớp" + ); + Assertions.assertNotNull(savedCustomer.id, "ID chưa được sinh ra"); + } +} diff --git a/account-service/src/test/java/com/drinkool/models/UserModelTest.java b/account-service/src/test/java/com/drinkool/models/UserModelTest.java index ad305cf..6073c7d 100644 --- a/account-service/src/test/java/com/drinkool/models/UserModelTest.java +++ b/account-service/src/test/java/com/drinkool/models/UserModelTest.java @@ -2,120 +2,149 @@ package com.drinkool.models; import static org.junit.jupiter.api.Assertions.*; -import com.drinkool.entities.CustomerEntity; -import io.quarkus.test.TestTransaction; import io.quarkus.test.junit.QuarkusTest; -import jakarta.persistence.*; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; import jakarta.transaction.Transactional; import jakarta.ws.rs.BadRequestException; -import java.util.*; +import java.util.Random; import org.junit.jupiter.api.Test; @Entity -@Table +@Table(name = "test_users") class TestUserEntity extends UserModel {} @QuarkusTest public class UserModelTest { + // Helper method để tạo số điện thoại ngẫu nhiên hợp lệ + private String generateValidPhone() { + return "+8477" + String.format("%07d", (new Random()).nextInt(10000000)); + } + + // 1. Test case: Đăng ký thành công (đầy đủ tham số) @Test @Transactional public void testSignupSuccess() { - //dang nhap thanh cong TestUserEntity user = new TestUserEntity(); - String phone = - "+8477" + String.format("%07d", (new Random()).nextInt(10000000)); + String phone = generateValidPhone(); String password = "02092006Danh"; + user.signup("Tran Huu Danh", phone, password); assertNotNull(user.id, "ID not null after persist"); assertEquals("Tran Huu Danh", user.name); assertEquals(phone, user.phone); - // assertNotNull(user.hashedPassword, "password phai hash"); + assertNotNull(user.hashedPassword, "Password phải được hash"); TestUserEntity savedUser = TestUserEntity.find( "phone", phone ).firstResult(); - assertNotNull(savedUser); + assertNotNull(savedUser, "Phải tìm thấy user trong database"); } - // 2. Test case: Đăng ký thất bại do số điện thoại đã tồn tại + // 2. Test case: Đăng ký thành công (chỉ dùng số điện thoại) + @Test + @Transactional + public void testSignupOneParamSuccess() { + TestUserEntity user = new TestUserEntity(); + String phone = generateValidPhone(); + + user.signup(phone); + + assertNotNull(user.id, "ID not null after persist"); + assertEquals(phone, user.name, "Tên mặc định phải là số điện thoại"); + assertEquals(phone, user.phone); + assertNotNull( + user.hashedPassword, + "Password tự động phải được tạo và hash" + ); + } + + // 3. Test case: Đăng ký thất bại do số điện thoại đã tồn tại @Test @Transactional public void testSignupFailExistedUser() { - TestUserEntity existUser = new TestUserEntity(); - String phone = - "+8477" + String.format("%07d", (new Random()).nextInt(10000000)); + String phone = generateValidPhone(); String password = "tungtungtungshahua"; + + // Tạo user đầu tiên + TestUserEntity existUser = new TestUserEntity(); existUser.signup("Nguyen Thanh Quy", phone, password); - CustomerEntity newCustomer = new CustomerEntity(); + // Tạo user thứ hai với cùng số điện thoại + TestUserEntity newUser = new TestUserEntity(); BadRequestException exception = assertThrows( BadRequestException.class, () -> { - newCustomer.signup("Nguyen Viet Anh", phone, "02092006Anh"); + newUser.signup("Nguyen Viet Anh", phone, "02092006Anh"); } ); assertEquals("ExistedUser", exception.getMessage()); } - // 3. Test case: Đăng ký thất bại do số điện thoại sai định dạng + // 4. Test case: Đăng ký thất bại do số điện thoại sai định dạng @Test - @TestTransaction + @Transactional public void testSignupFailInvalidPhoneNumber() { - TestUserEntity customer = new TestUserEntity(); + TestUserEntity user = new TestUserEntity(); String invalidPhone = "12345"; BadRequestException exception = assertThrows( BadRequestException.class, () -> { - customer.signup("Lam Vi Hoang", invalidPhone, "Pass123"); + user.signup("Lam Vi Hoang", invalidPhone, "Pass123"); } ); assertEquals("InvalidPhoneNumber", exception.getMessage()); } + // 5. Test case: Đăng nhập thành công @Test @Transactional public void testLoginSuccess() { - //dang nhap thanh cong TestUserEntity user = new TestUserEntity(); - String phone = - "+8477" + String.format("%07d", (new Random()).nextInt(10000000)); + String phone = generateValidPhone(); String password = "02092006Danh"; + user.signup("Tran Huu Danh", phone, password); - Boolean loginResult = user.login(password); + boolean loginResult = user.login(password); - assertTrue(loginResult); + assertTrue(loginResult, "Đăng nhập phải thành công với mật khẩu đúng"); } + // 6. Test case: Đăng nhập thất bại do sai mật khẩu @Test @Transactional public void testLoginFailWrongPassword() { - //dang nhap that bai TestUserEntity user = new TestUserEntity(); - String phone = - "+8477" + String.format("%07d", (new Random()).nextInt(10000000)); + String phone = generateValidPhone(); String truePassword = "02092006Danh"; String wrongPassword = "02092006danhh"; user.signup("Tran Huu Danh", phone, truePassword); - Boolean wrongTest = user.login(wrongPassword); - assertFalse(wrongTest); + boolean loginResult = user.login(wrongPassword); + + assertFalse(loginResult, "Đăng nhập phải thất bại với mật khẩu sai"); } + // 7. Test case: Đăng nhập thất bại do hashedPassword bị null @Test @Transactional public void testLoginFailNullHashedPassword() { TestUserEntity user = new TestUserEntity(); - user.hashedPassword = null; + user.hashedPassword = null; // Cố tình set null để test lỗi boolean loginResult = user.login("anhdomixi"); - assertFalse(loginResult); + + assertFalse( + loginResult, + "Đăng nhập phải thất bại nếu hashedPassword là null" + ); } } diff --git a/account-service/src/test/java/com/drinkool/services/OtpServiceTest.java b/account-service/src/test/java/com/drinkool/services/OtpServiceTest.java new file mode 100644 index 0000000..0057f97 --- /dev/null +++ b/account-service/src/test/java/com/drinkool/services/OtpServiceTest.java @@ -0,0 +1,86 @@ +package com.drinkool.services; + +import static org.junit.jupiter.api.Assertions.*; + +import io.quarkus.test.junit.QuarkusTest; +import jakarta.inject.Inject; +import java.util.Random; +import org.junit.jupiter.api.Test; + +@QuarkusTest +public class OtpServiceTest { + + @Inject + OtpService otpService; + + // 1. Test case: Tạo và lưu OTP thành công + @Test + public void testGenerateAndSaveOtp() { + String phone = + "+8477" + String.format("%07d", new Random().nextInt(10000000)); + String otp = otpService.generateAndSaveOtp(phone); + + assertNotNull(otp, "OTP sinh ra không được null"); + assertEquals(6, otp.length(), "OTP phải có độ dài đúng 6 ký tự"); + assertTrue(otp.matches("\\d{6}"), "OTP chỉ được chứa các chữ số"); + } + + // 2. Test case: Xác thực OTP thành công + @Test + public void testVerifyOtpSuccess() { + String phone = + "+8477" + String.format("%07d", new Random().nextInt(10000000)); + String generatedOtp = otpService.generateAndSaveOtp(phone); + + // Xác thực với OTP vừa tạo ra + boolean verifyResult = otpService.verifyOtp(phone, generatedOtp); + + assertTrue(verifyResult, "Xác thực phải thành công với OTP đúng"); + } + + // 3. Test case: Xác thực thất bại do sai mã OTP + @Test + public void testVerifyOtpFailWrongOtp() { + String phone = + "+8477" + String.format("%07d", new Random().nextInt(10000000)); + otpService.generateAndSaveOtp(phone); // Tạo OTP nhưng không dùng + + String wrongOtp = "999999"; // Giả sử mã sai + boolean verifyResult = otpService.verifyOtp(phone, wrongOtp); + + assertFalse(verifyResult, "Xác thực phải thất bại khi truyền sai mã OTP"); + } + + // 4. Test case: Xác thực thất bại do OTP không tồn tại (chưa tạo hoặc đã hết hạn) + @Test + public void testVerifyOtpFailNotExists() { + String unverifiedPhone = + "+8499" + String.format("%07d", new Random().nextInt(10000000)); + + // Cố tình xác thực cho một số điện thoại chưa từng gọi generateAndSaveOtp + boolean verifyResult = otpService.verifyOtp(unverifiedPhone, "123456"); + + assertFalse( + verifyResult, + "Xác thực phải thất bại với SĐT không có OTP trong Redis" + ); + } + + // 5. Test case: Đảm bảo OTP chỉ được sử dụng 1 lần (đã bị xóa sau khi verify thành công bằng lệnh getdel) + @Test + public void testVerifyOtpConsumedAfterSuccess() { + String phone = + "+8477" + String.format("%07d", new Random().nextInt(10000000)); + String generatedOtp = otpService.generateAndSaveOtp(phone); + + // Lần 1: Xác thực thành công + assertTrue(otpService.verifyOtp(phone, generatedOtp)); + + // Lần 2: Xác thực lại cùng mã đó sẽ thất bại vì key đã bị getdel xóa khỏi Redis + boolean secondVerifyResult = otpService.verifyOtp(phone, generatedOtp); + assertFalse( + secondVerifyResult, + "OTP không được phép tái sử dụng sau khi đã verify thành công" + ); + } +} diff --git a/account-service/src/test/java/com/drinkool/utils/PhoneValidatorTest.java b/account-service/src/test/java/com/drinkool/utils/PhoneValidatorTest.java new file mode 100644 index 0000000..10676a3 --- /dev/null +++ b/account-service/src/test/java/com/drinkool/utils/PhoneValidatorTest.java @@ -0,0 +1,53 @@ +package com.drinkool.utils; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class PhoneValidatorTest { + + @Test + public void testValidVietnamesePhoneNumbers() { + // Số điện thoại VN hợp lệ (có mã vùng hoặc số 0 ở đầu đều được) + Assertions.assertTrue( + PhoneValidator.isValidInternationalPhone("0987654321") + ); + Assertions.assertTrue( + PhoneValidator.isValidInternationalPhone("+84987654321") + ); + Assertions.assertTrue( + PhoneValidator.isValidInternationalPhone("84987654321") + ); + } + + @Test + public void testValidInternationalPhoneNumbers() { + // Số điện thoại quốc tế hợp lệ (Ví dụ: Mỹ +1) + Assertions.assertTrue( + PhoneValidator.isValidInternationalPhone("+14155552671") + ); + } + + @Test + public void testInvalidPhoneNumbers() { + // Chuỗi không phải số điện thoại + Assertions.assertFalse( + PhoneValidator.isValidInternationalPhone("not-a-phone-number") + ); + + // Số điện thoại quá ngắn hoặc sai định dạng + Assertions.assertFalse(PhoneValidator.isValidInternationalPhone("123")); + Assertions.assertFalse( + PhoneValidator.isValidInternationalPhone("099999999999999") + ); + + // Chuỗi rỗng + Assertions.assertFalse(PhoneValidator.isValidInternationalPhone("")); + } + + @Test + public void testNullPhoneNumber() { + // Cố tình truyền null để xem hàm xử lý thế nào + // Lưu ý: Đọc thêm phần "Mẹo nhỏ" bên dưới để pass được test này + Assertions.assertFalse(PhoneValidator.isValidInternationalPhone(null)); + } +} diff --git a/eatery-service/src/test/java/com/drinkool/entities/InventoryItemEntityTest.java b/eatery-service/src/test/java/com/drinkool/entities/InventoryItemEntityTest.java new file mode 100644 index 0000000..4aecfc0 --- /dev/null +++ b/eatery-service/src/test/java/com/drinkool/entities/InventoryItemEntityTest.java @@ -0,0 +1,50 @@ +package com.drinkool.entities; + +import io.quarkus.test.junit.QuarkusTest; +import jakarta.transaction.Transactional; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +@QuarkusTest +public class InventoryItemEntityTest { + + @Test + @Transactional + public void testCreateInventoryItem() { + // 1. Chuẩn bị dữ liệu (Lưu ý: Không còn biến quantity nữa) + String name = "Trà sữa truyền thống"; + String unit = "Ly"; + + // 2. Gọi hàm static create (Không dùng new hay hàm add nữa) + // Hàm này vừa tạo object, vừa gán data, vừa gọi persist() luôn + InventoryItemEntity item = InventoryItemEntity.create(name, unit); + + // 3. Kiểm chứng + Assertions.assertNotNull(item, "Entity tạo ra không được null"); + Assertions.assertEquals(name, item.name, "Tên không khớp"); + Assertions.assertEquals(unit, item.unit, "Đơn vị không khớp"); + Assertions.assertNotNull( + item.id, + "ID phải được sinh ra tự động sau khi persist" + ); + } + + @Test + @Transactional + public void testCreateWithDefaultUnit() { + // 1. Chuẩn bị dữ liệu + String name = "Trân châu đen"; + + // 2. Gọi hàm static create rút gọn (chỉ truyền name, unit sẽ tự là "unit") + InventoryItemEntity item = InventoryItemEntity.create(name); + + // 3. Kiểm chứng + Assertions.assertNotNull(item, "Entity tạo ra không được null"); + Assertions.assertEquals(name, item.name, "Tên không khớp"); + Assertions.assertEquals("unit", item.unit, "Unit mặc định phải là 'unit'"); + Assertions.assertNotNull( + item.id, + "ID phải được sinh ra tự động sau khi persist" + ); + } +} diff --git a/eatery-service/src/test/java/com/drinkool/entities/MenuItemEntityTest.java b/eatery-service/src/test/java/com/drinkool/entities/MenuItemEntityTest.java new file mode 100644 index 0000000..e6b62ac --- /dev/null +++ b/eatery-service/src/test/java/com/drinkool/entities/MenuItemEntityTest.java @@ -0,0 +1,90 @@ +package com.drinkool.entities; + +import static org.junit.jupiter.api.Assertions.*; + +import io.quarkus.test.junit.QuarkusTest; +import jakarta.transaction.Transactional; +import org.junit.jupiter.api.Test; + +@QuarkusTest +public class MenuItemEntityTest { + + @Test + @Transactional + public void testCreateMenuItemSuccessfully() { + // 1. Chuẩn bị dữ liệu (Arrange) + String name = "Cà phê sữa đá"; + double price = 29000.0; + + // 2. Thực thi (Act) - Gọi hàm tĩnh create + MenuItemEntity menuItem = MenuItemEntity.create(name, price); + + // 3. Kiểm chứng (Assert) + assertNotNull(menuItem, "Thực thể MenuItem không được null"); + assertEquals(name, menuItem.name, "Tên món ăn không khớp"); + assertEquals(price, menuItem.price, "Giá món ăn không khớp"); + assertNotNull(menuItem.id, "ID phải được tự động sinh ra sau khi persist"); + assertTrue( + menuItem.ingredients.isEmpty(), + "Danh sách nguyên liệu ban đầu phải rỗng" + ); + } + + @Test + @Transactional + public void testAddIngredientSuccessfully() { + // 1. Chuẩn bị dữ liệu (Arrange) + // Tạo món ăn + MenuItemEntity menuItem = MenuItemEntity.create( + "Trà sữa trân châu", + 45000.0 + ); + + // Tạo nguyên liệu trong kho (Dùng đúng hàm add() của EateryInventoryItemEntity như đã fix) + EateryInventoryItemEntity inventoryItem = EateryInventoryItemEntity.create( + "Trân châu đen", + 50, + "kg" + ); + + double requiredQty = 0.2; // Cần 0.2 kg trân châu cho 1 ly + + // 2. Thực thi (Act) - Thêm nguyên liệu vào món ăn + menuItem.addIngredient(inventoryItem, requiredQty); + + // 3. Kiểm chứng (Assert) + // Kiểm tra danh sách nguyên liệu của món ăn + assertFalse( + menuItem.ingredients.isEmpty(), + "Danh sách nguyên liệu không được rỗng sau khi thêm" + ); + assertEquals( + 1, + menuItem.ingredients.size(), + "Phải có đúng 1 nguyên liệu trong danh sách" + ); + + // Lấy phần tử nguyên liệu đầu tiên ra để kiểm tra chi tiết các liên kết + MenuItemIngredientEntity addedIngredient = menuItem.ingredients.get(0); + + assertNotNull( + addedIngredient.id, + "ID của Entity trung gian phải được sinh ra" + ); + assertEquals( + menuItem.id, + addedIngredient.menuItem.id, + "Khóa ngoại MenuItem bị sai" + ); + assertEquals( + inventoryItem.id, + addedIngredient.inventoryItem.id, + "Khóa ngoại InventoryItem bị sai" + ); + assertEquals( + requiredQty, + addedIngredient.requiredQuantity, + "Định lượng (requiredQty) không khớp" + ); + } +} diff --git a/eatery-service/src/test/java/com/drinkool/entities/MenuItemIngredientEntityTest.java b/eatery-service/src/test/java/com/drinkool/entities/MenuItemIngredientEntityTest.java index 75624ba..f9f84f8 100644 --- a/eatery-service/src/test/java/com/drinkool/entities/MenuItemIngredientEntityTest.java +++ b/eatery-service/src/test/java/com/drinkool/entities/MenuItemIngredientEntityTest.java @@ -23,14 +23,16 @@ public class MenuItemIngredientEntityTest { ); String inventoryItemName = "Water"; - double inventoryItemQuantity = 10; + int inventoryItemQuantity = 10; // Đổi thành int cho khớp với kiểu của EateryInventoryItemEntity String inventoryItemUnit = "l"; + // SỬA Ở ĐÂY: Khởi tạo bằng new và dùng hàm add() đúng như cấu trúc class của bạn EateryInventoryItemEntity inventoryItem = EateryInventoryItemEntity.create( inventoryItemName, inventoryItemQuantity, inventoryItemUnit ); + // Lưu ý: Hàm add() của bạn đã có sẵn lệnh this.persist() bên trong nên không cần gọi lại nữa! double requiredQuantity = 0.5; diff --git a/eatery-service/src/test/java/com/drinkool/entities/ReviewEntityTest.java b/eatery-service/src/test/java/com/drinkool/entities/ReviewEntityTest.java new file mode 100644 index 0000000..956b795 --- /dev/null +++ b/eatery-service/src/test/java/com/drinkool/entities/ReviewEntityTest.java @@ -0,0 +1,57 @@ +package com.drinkool.entities; + +import com.drinkool.models.ReviewModel; +import io.quarkus.test.junit.QuarkusTest; +import jakarta.transaction.Transactional; +import java.util.UUID; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +@QuarkusTest +public class ReviewEntityTest { + + @Test + @Transactional + public void testCreateReview() { + // 1. Chuẩn bị dữ liệu (Arrange) + UUID testReviewerId = UUID.randomUUID(); + UUID testEateryId = UUID.randomUUID(); + + // Truyền trực tiếp rating và comment vào constructor của ReviewModel + ReviewModel inputModel = new ReviewModel(5, "Món ăn tuyệt vời!"); + + // 2. Thực thi (Act) + ReviewEntity savedReview = ReviewEntity.create( + testReviewerId, + testEateryId, + inputModel + ); + + // 3. Kiểm chứng (Assert) + Assertions.assertNotNull( + savedReview, + "Thực thể Review sau khi tạo không được null" + ); + Assertions.assertEquals( + testReviewerId, + savedReview.reviewerId, + "Reviewer ID không khớp" + ); + Assertions.assertEquals( + testEateryId, + savedReview.eateryId, + "Eatery ID không khớp" + ); + Assertions.assertEquals(5, savedReview.rating, "Rating không khớp"); + Assertions.assertEquals( + "Món ăn tuyệt vời!", + savedReview.comment, + "Comment không khớp" + ); + + Assertions.assertNotNull( + savedReview.id, + "ID phải được tự động sinh ra sau khi persist" + ); + } +} diff --git a/eatery-service/src/test/java/com/drinkool/services/ReviewServiceTest.java b/eatery-service/src/test/java/com/drinkool/services/ReviewServiceTest.java index e1b49ae..69c5c34 100644 --- a/eatery-service/src/test/java/com/drinkool/services/ReviewServiceTest.java +++ b/eatery-service/src/test/java/com/drinkool/services/ReviewServiceTest.java @@ -35,7 +35,8 @@ public class ReviewServiceTest { .body(input) .when() .post("/api/review/create") - .then(); + .then() + .statusCode(201); assertEquals(1, ReviewEntity.find("reviewerId", reviewerId).count()); } diff --git a/eatery-service/src/test/resources/application.properties b/eatery-service/src/test/resources/application.properties index 59bc331..42128a9 100644 --- a/eatery-service/src/test/resources/application.properties +++ b/eatery-service/src/test/resources/application.properties @@ -7,4 +7,4 @@ quarkus.http.host=0.0.0.0 %test.quarkus.datasource.jdbc.url=jdbc:h2:mem:test_db;DB_CLOSE_DELAY=-1 ## Orm -%test.quarkus.hibernate-orm.database.generation=drop-and-create +%test.quarkus.hibernate-orm.database.generation=drop-and-create \ No newline at end of file diff --git a/gateway-service/src/main/resources/application.properties b/gateway-service/src/main/resources/application.properties index dffba85..43aa152 100644 --- a/gateway-service/src/main/resources/application.properties +++ b/gateway-service/src/main/resources/application.properties @@ -8,4 +8,4 @@ quarkus.container-image.builder=docker quarkus.container-image.additional-tags=latest # Build -quarkus.native.container-build=false \ No newline at end of file +quarkus.native.container-build=false diff --git a/lib/src/main/java/com/drinkool/models/ReviewModel.java b/lib/src/main/java/com/drinkool/models/ReviewModel.java index 30d20e9..58c4279 100644 --- a/lib/src/main/java/com/drinkool/models/ReviewModel.java +++ b/lib/src/main/java/com/drinkool/models/ReviewModel.java @@ -13,13 +13,13 @@ public class ReviewModel extends BaseEntity { @Column public String comment; - public ReviewModel() {} - public ReviewModel(int rating, String comment) { this.rating = rating; this.comment = comment; } + public ReviewModel() {} + public ReviewModel(ReviewModel input) { this(input.rating, input.comment); }