diff --git a/.github/workflows/pull_request_workflow.yml b/.github/workflows/pull_request_workflow.yml index 302dcf1e5..e0d5b9642 100644 --- a/.github/workflows/pull_request_workflow.yml +++ b/.github/workflows/pull_request_workflow.yml @@ -48,10 +48,14 @@ jobs: java-version: 21 distribution: 'corretto' + - name: run flyway + run: ./gradlew --no-daemon --console=plain flywayMigrate + - name: gradle build env: DB_URL: ${{ secrets.DB_URL }} run: ./gradlew --no-daemon --console=plain build + - name: Publish Unit Test Results uses: EnricoMi/publish-unit-test-result-action@v2 diff --git a/build.gradle.kts b/build.gradle.kts index ac5dec817..a5806a91f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,11 +2,22 @@ import org.gradle.api.tasks.testing.logging.TestExceptionFormat import org.gradle.api.tasks.testing.logging.TestLogEvent plugins { + id("org.flywaydb.flyway") version "10.4.1" kotlin("jvm") version "1.9.25" kotlin("plugin.spring") version "1.9.25" id("org.springframework.boot") version "3.3.3" id("io.spring.dependency-management") version "1.1.6" } +buildscript { + dependencies { + classpath("org.flywaydb:flyway-database-postgresql:10.4.1") + } +} + +flyway { + url = "jdbc:postgresql://localhost:5432/academy?user=bob&password=dev" + driver = "org.postgresql.Driver" +} java { toolchain { @@ -61,8 +72,11 @@ tasks { if (dbUrl != null && dbUrl.isNotBlank()) { systemProperty("DB_URL", dbUrl) } else { - systemProperty("DB_URL", "jdbc:postgresql://localhost:5432/email_service?user=bob&password=dev") + systemProperty("DB_URL", "jdbc:postgresql://localhost:5432/academy?user=bob&password=dev") } systemProperty("spring.profiles.active", "development,test") } + bootRun { + mainClass.set("com.hibob.AcademyApplicationKt") // Replace with your actual main class + } } diff --git a/settings.gradle.kts b/settings.gradle.kts index acb3e0f79..65e18a6bd 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1 +1,3 @@ rootProject.name = "academy" + + diff --git a/src/main/kotlin/Types/Tamir/StoreService.kt b/src/main/kotlin/Types/Tamir/StoreService.kt new file mode 100644 index 000000000..4ec24565b --- /dev/null +++ b/src/main/kotlin/Types/Tamir/StoreService.kt @@ -0,0 +1,14 @@ +package com.hibob.Types.Tamir + +import org.springframework.stereotype.Component +import java.time.LocalDate + +@Component +class StoreService { + // 7. implement pay method + fun pay(cart: List, payment: Payment): Map { + return cart.associate { + it.clientId to checkout(it, payment) + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/Types/Tamir/types.kt b/src/main/kotlin/Types/Tamir/types.kt new file mode 100644 index 000000000..2509775d0 --- /dev/null +++ b/src/main/kotlin/Types/Tamir/types.kt @@ -0,0 +1,89 @@ +package com.hibob.Types.Tamir +import java.time.LocalDate + +//1. create data class of Cart that include: client Id and list of Products +data class Cart(val clientId: String, val products: List) +//2. product object contain id, name, price and custom - custom can be either int, string or any other type +data class Product(val id: String, val name: String,val price: Double, val custom: Any) +//3. create sealed class of payment that can be use by the following classes: +// CreditCard - contains number, expiryDate, type and limit (type can be VISA, MASTERCARD, DISCOVER and AMERICAN_EXPRESS) +// PayPal - contain email +// Cash - without args +sealed class Payment{ + data class CreditCard(val number: String, val expiryDates: LocalDate,val types: CreditCardType,val limit:Double) : Payment() + { + fun checkCreditCardLenValidation(): Boolean { + return (this.number.length == 10) + } + fun checkExpiryDateValidation(): Boolean { + return (this.expiryDates.isAfter(LocalDate.now())) + } + fun checkIfLimitAboveCheckout(checkOutSum:Double): Boolean { + return checkOutSum < this.limit + } + fun checkCardTypeValidate(type: CreditCardType):Boolean{ + return when(type){ + CreditCardType.VISA -> true + CreditCardType.MASTERCARD-> true + CreditCardType.DISCOVER-> false + CreditCardType.AMERICAN_EXPRESS-> false + } + } + } + data class PayPal(val email: String) : Payment() + { + fun checkPayPalValidation(): Boolean { + return (this.toString().contains('@')) + } + } + data object Cash : Payment() +} +enum class CreditCardType { + VISA, MASTERCARD, DISCOVER, AMERICAN_EXPRESS +} +//4. add fail function that get message an argument and throw IllegalStateException +fun fail(message: String):Nothing{ + throw IllegalStateException(message) +} +//5. write function check if custom is true and only if its true its valid product. +fun checkIfCustomIsValid(custom: Any):Boolean { + return (custom as? Boolean) ?: false +} +//6. write function called checkout and get cart and payment that pay the money +// * only custom with true are valid +// * cash payment is not valid to pay so if the payment is cash use fail function +// * in case of credit card need to validate the expiryDate is after the current date + limit is bigger than the total we need to pay and we allow to use only VISA or MASTERCARD +// * in case of payPal validate we hae @ +/// * the return value of this function, should be a data class with employee id, status (success or FAILURE) and total called Check +fun checkout(cart: Cart,payment: Payment):Check{ + val total = cart.products + .filter { product -> checkIfCustomIsValid(product.custom) } + .sumOf { product -> product.price } + if (checkPaymentMethod(payment,total)) { + return Check(cart.clientId, Statuses.SUCCESS, total) + } + return Check(cart.clientId, Statuses.FAILURE, 0.0) +} + +fun checkPaymentMethod(payment: Payment,price: Double):Boolean{ + return when (payment) { + is Payment.PayPal -> { + payment.checkPayPalValidation() + } + is Payment.CreditCard -> { + (payment.checkCreditCardLenValidation() && + payment.checkIfLimitAboveCheckout(price)&& + payment.checkCardTypeValidate(payment.types)&& + payment.checkExpiryDateValidation()) + } + is Payment.Cash -> { + fail("Error... You can't use CASH...") + } + } +} + +data class Check(val employeeId: String, val statuses:Statuses,val total:Double) +enum class Statuses { + SUCCESS,FAILURE +} + diff --git a/src/main/kotlin/com/hibob/KotlinBasics/extentions.kt b/src/main/kotlin/com/hibob/academy/basics/extentions.kt similarity index 84% rename from src/main/kotlin/com/hibob/KotlinBasics/extentions.kt rename to src/main/kotlin/com/hibob/academy/basics/extentions.kt index 6e832f1b1..b4aa864d5 100644 --- a/src/main/kotlin/com/hibob/KotlinBasics/extentions.kt +++ b/src/main/kotlin/com/hibob/academy/basics/extentions.kt @@ -1,4 +1,4 @@ -package com.hibob.KotlinBasics +package com.hibob.academy.basics //Q1 fun List.sum():Int{ var result = 0 @@ -8,7 +8,7 @@ fun List.sum():Int{ return result } -fun main(args:Array){ +fun main12(args:Array){ val sum = listOf(1,2,3).sum() println(sum) //6 println(3 toPowerOf -2) diff --git a/src/main/kotlin/Omri/Basics/LoopsOmriLec02.kt b/src/main/kotlin/com/hibob/academy/basicsomri/LoopsOmriLec02.kt similarity index 93% rename from src/main/kotlin/Omri/Basics/LoopsOmriLec02.kt rename to src/main/kotlin/com/hibob/academy/basicsomri/LoopsOmriLec02.kt index 0b6fd30ca..55cf618fc 100644 --- a/src/main/kotlin/Omri/Basics/LoopsOmriLec02.kt +++ b/src/main/kotlin/com/hibob/academy/basicsomri/LoopsOmriLec02.kt @@ -1,4 +1,4 @@ -package Omri.Basics +package com.hibob.academy.basicsomri //Omri Solution fun isValidIdentifier(s: String): Boolean { @@ -28,7 +28,7 @@ fun isValidIdentifier(s: String): Boolean { //} -fun main(args:Array){ +fun main11(args:Array){ println(isValidIdentifier("name"))//true println(isValidIdentifier("_name"))//true println(isValidIdentifier("_12"))//true diff --git a/src/main/kotlin/com/hibob/academy/nullability/Q1.kt b/src/main/kotlin/com/hibob/academy/nullability/Q1.kt new file mode 100644 index 000000000..a46e66f62 --- /dev/null +++ b/src/main/kotlin/com/hibob/academy/nullability/Q1.kt @@ -0,0 +1,21 @@ +package com.hibob.academy.nullability + +/** + * Modify the main function to print each user's email safely. + * Use the Elvis operator to provide the "Email not provided" default string if the email is null. + * Ensure your solution handles both user1 and user2 correctly. + */ +data class User(val name: String?, val email: String?) + +fun main2() { + val user1: User = User("Alice", null) + val user2: User = User(null, "alice@example.com") + + // Task: Print user email or "Email not provided" if null + println(getUserEmail(user1.email)) + println(getUserEmail(user2.email)) +} + +fun getUserEmail(email: String?):String { + return email ?: "Email not provided" +} diff --git a/src/main/kotlin/com/hibob/academy/nullability/Q2.kt b/src/main/kotlin/com/hibob/academy/nullability/Q2.kt new file mode 100644 index 000000000..248fb2015 --- /dev/null +++ b/src/main/kotlin/com/hibob/academy/nullability/Q2.kt @@ -0,0 +1,14 @@ +package com.hibob.academy.nullability +/** + * Modify the main function to calculate and print the sum of all non-null integers in the list numbers. + * Use safe calls and/or the Elvis operator where appropriate. + **/ +fun main3() { + val numbers: List = listOf(1, null, 3, null, 5) + // Task: Calculate the sum of all non-null numbers + println("The sum of all non-null numbers is: ${calculateListSum(numbers)}") +} + +fun calculateListSum(numbers: List ): Int{ + return numbers.sumOf { it ?: 0 } +} \ No newline at end of file diff --git a/src/main/kotlin/com/hibob/academy/nullability/Q3.kt b/src/main/kotlin/com/hibob/academy/nullability/Q3.kt new file mode 100644 index 000000000..5f98aa558 --- /dev/null +++ b/src/main/kotlin/com/hibob/academy/nullability/Q3.kt @@ -0,0 +1,14 @@ +package com.hibob.academy.nullability +/** + * Write an extension function nullSafeToUpper() for String? that converts the string + * to uppercase if it is not null, or returns "NO TEXT PROVIDED" if it is null. + * Apply this function in the main function to handle the variable text. + * + **/ +fun main4() { + val text: String? = null + // Task: Create and use an extension function to print text in uppercase if it's not null, or "NO TEXT PROVIDED" if it is null. + println(text.printTextInUppercase()) +} + +fun String?.printTextInUppercase(): String = this?.uppercase() ?: "NO TEXT PROVIDED" diff --git a/src/main/kotlin/com/hibob/academy/nullability/Q4.kt b/src/main/kotlin/com/hibob/academy/nullability/Q4.kt new file mode 100644 index 000000000..a44bfee76 --- /dev/null +++ b/src/main/kotlin/com/hibob/academy/nullability/Q4.kt @@ -0,0 +1,25 @@ +package com.hibob.academy.nullability +/** + * Modify the main function to iterate over the employees list and print each employee's city. + * Use safe calls (?.) and the Elvis operator (?:) to ensure that if an employee or their address is null, + * or if the city is null, "City Unknown" is printed instead. + * Try to write the solution in the most concise way possible using Kotlin's chaining capabilities. + * + **/ +data class Address(val city: String?, val street: String?) +data class Employee(val name: String?, val address: Address?) + +fun main5() { + val employees = listOf( + Employee("John", Address("New York", "Fifth Ave")), + Employee("Jane", null), + Employee(null, Address(null, "Unknown Street")), + Employee("Alice", Address("Los Angeles", null)) + ) + + // Task: Print each employee's city safely. If the city is not available, print "City Unknown". + employees.printsAllEmployeesCites() +} + +fun List.printsAllEmployeesCites() = this.forEach( { println(it.address?.city ?: "City Unknown") }) + diff --git a/src/main/kotlin/com/hibob/academy/nullability/Q5.kt b/src/main/kotlin/com/hibob/academy/nullability/Q5.kt new file mode 100644 index 000000000..e66e3fe96 --- /dev/null +++ b/src/main/kotlin/com/hibob/academy/nullability/Q5.kt @@ -0,0 +1,33 @@ +package com.hibob.academy.nullability + +/** + * Iterate through the list of products. + * Use the ?.let function to safely access the name and price of each product. + * Print the product details only if both name and price are non-null. Format the output as "Product: [name] - $[price]". + * If either name or price is null, do not print anything for that product. + * + */ + +data class Product(val name: String?, val price: Double?) + +fun main6() { + val products = listOf( + Product("Laptop", 999.99), + Product(null, 299.99), + Product("Smartphone", null), + Product(null, null) + ) + + // Task: Print the details of products only if both name and price are not null. + products.printsAllProductsDetails() +} + +fun List.printsAllProductsDetails() { + this.forEach({product -> + product?.name?.let { + product.price?.let { + println("Product: ${product.name} - ${product.price}") + } + } + }) +} diff --git a/src/main/kotlin/com/hibob/academy/nullability/Q6.kt b/src/main/kotlin/com/hibob/academy/nullability/Q6.kt new file mode 100644 index 000000000..a42abb349 --- /dev/null +++ b/src/main/kotlin/com/hibob/academy/nullability/Q6.kt @@ -0,0 +1,28 @@ +package com.hibob.academy.nullability + +data class Department(val name: String?, val manager: EmployeeDetails?) +data class EmployeeDetails(val name: String?, val contactInfo: ContactInfo?) +data class ContactInfo(val email: String?, val phone: String?) + +fun main7() { + val departments = listOf( + Department("Engineering", EmployeeDetails("Alice", ContactInfo("alice@example.com", null))), + Department("Human Resources", null), + Department(null, EmployeeDetails("Bob", ContactInfo(null, "123-456-7890"))), + Department("Marketing", EmployeeDetails(null, ContactInfo("marketing@example.com", "987-654-3210"))) + ) + + // Task: Print each department's name and manager's contact information. + // If any information is missing, use appropriate defaults. + departments.printEachDepartmentNameAndContactInfo() +} + +fun List.printEachDepartmentNameAndContactInfo() { + this.forEach { + department -> println("Department name is: " + + "${department?.name?.let{ "${it}" } ?: "Scooby Doo!"}" + + " | Employee Details are: name: " + "${department?.manager?.name?.let { "${it}" } ?: "Ron Azar"}" + + " | Contact info: email: " + "${department?.manager?.contactInfo?.email?.let { "${it}" } ?: "Ron.Azar@hibob.io"}" + + " | Phone number: " + "${department?.manager?.contactInfo?.phone?.let { "${it}" } ?: "054-9423644"}" + )} +} \ No newline at end of file diff --git a/src/main/kotlin/com/hibob/academy/nullability/Q7.kt b/src/main/kotlin/com/hibob/academy/nullability/Q7.kt new file mode 100644 index 000000000..20c4cb1ee --- /dev/null +++ b/src/main/kotlin/com/hibob/academy/nullability/Q7.kt @@ -0,0 +1,52 @@ +package com.hibob.academy.nullability + +/** + * + * Iterate through the list of customers, which may contain null entries. + * For each non-null customer, print the customer's name or "Name Unknown" if the name is null. + * Print the account ID or "Account ID Unknown" if the account or ID is null. + * Print the account balance or "Balance Not Available" if the account details or balance is null. + * If the customer object itself is null, print "Customer data is not available." + * + */ +data class Customer(val name: String?, val account: Account?) +data class Account(val id: String?, val details: AccountDetails?) +data class AccountDetails(val type: String?, val balance: Double?) + +fun initializeNullableCustomers(): List { + return listOf( + Customer("John Doe", Account("12345", AccountDetails("Checking", 1500.00))), + null, + Customer("Jane Smith", Account("67890", AccountDetails(null, 780.00))), + Customer(null, Account(null, AccountDetails("Savings", null))), + null, + Customer("Emily White", null) + ) +} + +fun main8() { + val customers = initializeNullableCustomers() + + // Task: Print each customer's name, account ID, and account balance. Handle all null cases appropriately. + customers.printAllCustomersDetails() +} + +fun List.printAllCustomersDetails(){ + this.forEach {costumer-> + var costumerName = "Unknown name" + var accountId = "Unknown Id" + var accountType = "Unknown Type" + var accountBalance = 0.00 + costumer?.let { + costumer.name?.let { costumerNm -> costumerName = costumerNm } + costumer.account?.let { costumerAccount -> + costumerAccount.id?.let { costumerId -> accountId = costumerId } + costumerAccount.details?.let { costumerDetails -> + costumerDetails.type?.let { costumerType -> accountType=costumerType } + costumerDetails.balance?.let { costumerBalance-> accountBalance= costumerBalance } + } + } + } + println(Customer(costumerName, Account(accountId, AccountDetails(accountType,accountBalance)))) + } +} diff --git a/src/main/kotlin/com/hibob/academy/nullability/Q8.kt b/src/main/kotlin/com/hibob/academy/nullability/Q8.kt new file mode 100644 index 000000000..9c11b86e0 --- /dev/null +++ b/src/main/kotlin/com/hibob/academy/nullability/Q8.kt @@ -0,0 +1,101 @@ +package com.hibob.academy.nullability +/** +* Instructions: +* +* Traverse through the company structure starting from departments to teams and finally to team members. +* For each level (company, department, team, leader, members), check for null values and print appropriate information. +* Ensure that every piece of information printed includes a fallback for null values using the Elvis operator and that +* blocks of code dependent on non-null values are executed using ?.let. +* +*/ +data class Company(val name: String?, val departments: List?) +data class DepartmentDetails(val name: String?, val teams: List?) +data class Team(val name: String?, val leader: Leader?, val members: List?) +data class Leader(val name: String?, val title: String?) +data class Member(val name: String?, val role: String?) + +fun initializeCompany(): Company { + return Company( + "Tech Innovations Inc.", + listOf( + DepartmentDetails("Engineering", listOf( + Team("Development", Leader("Alice Johnson", "Senior Engineer"), listOf(Member("Bob Smith", "Developer"), null)), + Team("QA", Leader(null, "Head of QA"), listOf(Member(null, "QA Analyst"), Member("Eve Davis", null))), + null + )), + DepartmentDetails(null, listOf( + Team("Operations", null, listOf(Member("John Doe", "Operator"), Member("Jane Roe", "Supervisor"))) + )), + null + ) + ) +} + +fun main9() { + val company = initializeCompany() + + // Task: Print detailed information about each department, team, and team members, handling all null values appropriately. + printCompanyDetails(company) +} + +fun printCompanyDetails(company: Company) { + company?.let { + val companyName = it.name ?: "Unknown Company name" + print("| Company Name: $companyName Company information:") + printDepartmentsDetails(it.departments) + println() + }?: println("| Unknown company |") +} + + +fun printDepartmentsDetails(departments: List?){ + departments?.let { it.forEach{ department-> + department?.let { + val departmentName = department.name ?: "Unknown Department name" + println("| Department Name: $departmentName Department information: ") + printTeamsDetails(department.teams) + }?:println("| Unknown Department |") + }?:print("| Unknown Departments |") +} +} + +fun printTeamsDetails(teams: List?){ + teams?.let { + it.forEach{ team-> + team?.let { + val teamName = team.name ?: "Unknown Team name" + println("§ Team name: $teamName , Info about the team:") + printLeaderDetails(team.leader) + printMembersDetails(team.members) + }?: println("§ Unknown Team §") + }?:print("| Unknown Teams |") + } +} + +fun printLeaderDetails(leader: Leader?){ + leader?.let { + val leaderName = leader.name ?: "Unknown Leader Name" + val leaderTitle = leader.title ?: "Unknown Leader Title" + println("± Leader name: $leaderName & Leader Title: $leaderTitle ±") + }?: println("± Unknown Leader ±") +} + +fun printMembersDetails(members: List?){ + members?.let { + it.forEach{ member-> + member?.let { + val memberName = member.name ?: "Unknown Member name" + val memberRole = member.role ?: "Unknown Member role" + println("# member Role: $memberName & member Role: $memberRole #") + }?: println("# Unknown Member #") + }?:print("| Unknown Members |") + } +} + + + + + + + + diff --git a/src/main/kotlin/com/hibob/academy/nullability/Q9.kt b/src/main/kotlin/com/hibob/academy/nullability/Q9.kt new file mode 100644 index 000000000..003fff572 --- /dev/null +++ b/src/main/kotlin/com/hibob/academy/nullability/Q9.kt @@ -0,0 +1,78 @@ +package com.hibob.academy.nullability + +data class DepartmentData(val name: String?, val manager: EmployeeData?) +data class EmployeeData(val name: String?, val contactInfo: Contact?) +data class Contact(val email: String?, val phone: String?) + +fun main10() { + val departments = listOf( + DepartmentData("Engineering", EmployeeData("Alice", Contact("alice@example.com", "123-456-7890"))), + DepartmentData("Human Resources", null), + DepartmentData("Operations", EmployeeData("Bob", Contact(null, "234-567-8901"))), + DepartmentData("Marketing", EmployeeData(null, Contact("marketing@example.com", "345-678-9012"))), + DepartmentData("Finance", EmployeeData("Carol", Contact("", "456-789-0123"))) + ) + + // Implement the features here. +// * Filter Departments: Identify departments that have either no manager assigned or where the manager's +// * contact information is entirely missing. + val filterDepartmentsList = filterDepartments(departments) + println("Filtered departments: $filterDepartmentsList") + +// * Email List Compilation: Generate a list of all unique manager emails but exclude any null +// * or empty strings. Ensure the list has no duplicates. + val emailsList = makeManagersEmailsList(filterDepartmentsList) + println("emails: $emailsList") + +// * Reporting: For each department, generate a detailed report that includes the +// * department name, manager's name, email, and formatted phone number. +// * If any information is missing, provide a placeholder. + printsReport(departments) +} + +fun filterDepartments(departments: List): List { + return departments.filter { department -> + department.manager?.let { department.manager.contactInfo?.let { true }?:false } ?: false + } +} + +fun makeManagersEmailsList(departments: List):List{ + return departments.mapNotNull { department-> + department.manager?.contactInfo?.email?.let { email -> email} + }.distinct() +} + +// * Reporting: For each department, generate a detailed report that includes the +// * department name, manager's name, email, and formatted phone number. +// * If any information is missing, provide a placeholder. +fun printsReport(departments: List){ + departments.forEach { department-> + val departmentName = department.name ?: "Unknown Department Name!" + val departmentManagerName = department.manager?.name ?: "Unknown Department Manager Name!" + val departmentManagerEmail = department.manager?.contactInfo?.email ?: "Unknown Department Manager Email!" + val departmentManagerPhoneNumber = formatPhoneNumber(department.manager?.contactInfo?.phone ?: "") + printReportFormat(departmentName, departmentManagerName, departmentManagerEmail, departmentManagerPhoneNumber) + } +} + +fun printReportFormat(departmentName: String, departmentManagerName: String, departmentManagerEmail: String, departmentManagerPhoneNumber: String) { + println("±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±"+ + "\n|\t Department Name: $departmentName"+ + "\n|\t Department Manager Name: $departmentManagerName"+ + "\n|\t Department Manager Email: $departmentManagerEmail"+ + "\n|\t Department Manager Phone: $departmentManagerPhoneNumber"+ + "\n±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±") +} + +fun formatPhoneNumber(phoneNumber: String): String{ +val cleanPhoneNumber = phoneNumber.replace("-", "") + if (cleanPhoneNumber.length == 10) { + return String.format( + "(+%s) %s-%s", + cleanPhoneNumber.substring(0, 3), + cleanPhoneNumber.substring(3, 6), + cleanPhoneNumber.substring(6, 10) + ) + } + return "Unknown Department Manager Phone!" +} \ No newline at end of file diff --git a/src/main/kotlin/com/hibob/academy/omri/SequenceClassEx.kt b/src/main/kotlin/com/hibob/academy/omri/SequenceClassEx.kt new file mode 100644 index 000000000..71c98a85e --- /dev/null +++ b/src/main/kotlin/com/hibob/academy/omri/SequenceClassEx.kt @@ -0,0 +1,89 @@ +package com.hibob.academy.omri +import org.springframework.cglib.core.Predicate +import kotlin.system.exitProcess + +fun main14(args: Array) { + ex1() + ex2() + ex3() + ex4() + ex5() +} + +fun ex1() { + // change the program to + // 1. reuse the filter / map function + // 2. println each call to track the diffs between List and Seq + + val list = listOf(1, 2, 3, 4) + + val maxOddSquare = list + .map {println("Square: $it") + square(it) } + .filter { println("Is even: $it") + isEven(it) } + .find { println("Check if $it equal to four") + equalToFour(it) } + + val maxOddSquare2 = list.asSequence() + .map {println("Square: $it") + square(it) } + .filter { println("Is even: $it") + isEven(it) } + .find { println("Check if $it equal to four") + equalToFour(it) } +} + +fun isEven(x: Int) = x % 2 == 0 +fun square(n: Int) = n * n +fun equalToFour(n: Int) = n == 4 + + +fun ex2() { + // how many times filterFunc was called + fun filterFunc(it: Int): Boolean { + println("filterFunc was called") + return it < 3 + } + sequenceOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) + .filter { filterFunc(it) } + //Foreach is not included so its: 0 +} + +fun ex3() { + // create the list of the first 10 items of (2, 4, 8, 16, 32 ...) seq + val twoSq = generateSequence(2){ i: Int -> i * 2 }.take(10).forEach(::println) +} + +fun ex4() { + // create the list of the first 10 items of the Fibonacci seq + val fibonacci = generateSequence(Pair(0,1)) { Pair(it.second,(it.first+it.second)) }.map { it.first }.take(10).forEach { + print("$it ") + } +} + +fun ex5() { + // try to minimize the number of operations: + val engToHeb: Map = mapOf("today" to "היום", + "was" to "היה", + "good" to "טוב", + "day" to "יום", + "for" to "בשביל", + "walking" to "ללכת", + "in" to "ב", + "the" to "ה", + "park" to "פארק", + "sun" to "שמש", + "was" to "הייתה", + "shining" to "זורחת", + "and" to "ו", + "birds" to "ציפורים", + "were" to "היו", + "chirping" to "מצייצות") // assume the dictionary is real :-) + val b = "today was a good day for walking in the part. Sun was shining and birds were chirping" + .splitToSequence(" ") + .mapNotNull {engToHeb[it]} + .filter {it.length <= 3 } + .take(5) + .count() > 4 +} \ No newline at end of file diff --git a/src/main/kotlin/Properties/Tamir/PropertiesTamirClassEx.kt b/src/main/kotlin/com/hibob/academy/properties/PropertiesTamirClassEx.kt similarity index 96% rename from src/main/kotlin/Properties/Tamir/PropertiesTamirClassEx.kt rename to src/main/kotlin/com/hibob/academy/properties/PropertiesTamirClassEx.kt index 8e2090b01..0090e649b 100644 --- a/src/main/kotlin/Properties/Tamir/PropertiesTamirClassEx.kt +++ b/src/main/kotlin/com/hibob/academy/properties/PropertiesTamirClassEx.kt @@ -1,4 +1,4 @@ -package Properties.Tamir +package com.hibob.academy.properties import java.time.DayOfWeek //1. create class called Store that initlize by day and list of products diff --git a/src/main/kotlin/Basics/Tamir/TamirLec01 class exercise.kt b/src/main/kotlin/com/hibob/academy/tamir/TamirLec01 class exercise.kt similarity index 94% rename from src/main/kotlin/Basics/Tamir/TamirLec01 class exercise.kt rename to src/main/kotlin/com/hibob/academy/tamir/TamirLec01 class exercise.kt index f7e5073cb..766695ff0 100644 --- a/src/main/kotlin/Basics/Tamir/TamirLec01 class exercise.kt +++ b/src/main/kotlin/com/hibob/academy/tamir/TamirLec01 class exercise.kt @@ -1,5 +1,5 @@ -package Basics.Tamir -fun main() { +package com.hibob.academy.tamir +fun main14() { //Q1 printHelloWorld() //Q2 diff --git a/src/main/kotlin/com/hibob/academy/unitests/keren/BankAccount.kt b/src/main/kotlin/com/hibob/academy/unitests/keren/BankAccount.kt new file mode 100644 index 000000000..7d12bc02a --- /dev/null +++ b/src/main/kotlin/com/hibob/academy/unitests/keren/BankAccount.kt @@ -0,0 +1,26 @@ +package com.hibob.bootcamp +class BankAccount(private var balance: Double) { + + fun deposit(amount: Double): Double { + if (amount <= 0) { + throw IllegalArgumentException("Deposit amount must be greater than zero") + } + balance += amount + return balance + } + + fun withdraw(amount: Double): Double { + if (amount > balance) { + throw IllegalArgumentException("Insufficient balance") + } + if (amount <= 0) { + throw IllegalArgumentException("Withdraw amount must be greater than zero") + } + balance -= amount + return balance + } + + fun getBalance(): Double { + return balance + } +} diff --git a/src/main/kotlin/com/hibob/academy/unitests/keren/ListManager.kt b/src/main/kotlin/com/hibob/academy/unitests/keren/ListManager.kt new file mode 100644 index 000000000..48a6c6295 --- /dev/null +++ b/src/main/kotlin/com/hibob/academy/unitests/keren/ListManager.kt @@ -0,0 +1,78 @@ +package com.hibob.academy.unitests.keren + +//** +//* Exercise Instructions: +//* +//* Write tests for addPerson method: +//* +//* Test adding a unique person. +//* Test adding a duplicate person and ensure it throws the expected exception. +//* Test adding multiple people, checking that the list grows appropriately. +//* +//* +//* Write tests for removePerson method: +//* +//* Test removing a person that exists in the list. +//* Test trying to remove a person that does not exist, ensuring it returns false. +//* Test the state of the list after multiple add and remove operations. +//* +//* +//* Write tests for getPeopleSortedByAgeAndName method: +//* +//* Test with an empty list. +//* Test with one person. +//* Test with multiple people to ensure they are sorted first by age, then by name. +//* Test with edge cases like people with the same name but different ages and vice versa. +//* +//*/ +data class Person(val name: String, val age: Int) + +data class PeopleStatistics( + val averageAge: Double, + val youngest: Person, + val oldest: Person, + val ageCount: Map +) + +class ListManager { + private val people: MutableList = mutableListOf() + + fun addPerson(person: Person): Boolean { + if (people.any { it.name == person.name && it.age == person.age }) { + throw IllegalArgumentException("Duplicate person cannot be added.") + } + return people.add(person) + } + + fun getPeopleCount(): Int{ + return people.size + } + + fun containManger(person: Person): Boolean { + return people.contains(person) + } + + fun removePerson(person: Person): Boolean { + return people.remove(person) + } + + fun getPeopleSortedByAgeAndName(): List { + return people.sortedWith(compareBy { it.age }.thenBy { it.name }) + } + + fun calculateStatistics(): PeopleStatistics? { + if (people.isEmpty()) { + return null + } + val averageAge = people.map { it.age }.average()//Avarage list age + val youngest = people.minByOrNull { it.age }//Get the minimum age at the list or null + val oldest = people.maxByOrNull { it.age }//Get the maximum age at the list or null + val ageCount = people.groupingBy { it.age }.eachCount()//Return a map + + return youngest?.let { young -> + oldest?.let { old -> + PeopleStatistics(averageAge, young, old, ageCount) + } + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/hibob/academy/unitests/keren/userService.kt b/src/main/kotlin/com/hibob/academy/unitests/keren/userService.kt new file mode 100644 index 000000000..e61caffcc --- /dev/null +++ b/src/main/kotlin/com/hibob/academy/unitests/keren/userService.kt @@ -0,0 +1,60 @@ +package com.hibob.bootcamp.unittests + +data class User(val id: Long, val name: String, val email: String, val password: String, val isEmailVerified: Boolean = false) + +interface UserDao { + fun findById(userId: Long): User? + fun save(user: User): Boolean + fun update(user: User): Boolean +} + +interface NotificationService { + fun sendEmail(email: String, message: String): Boolean +} + +interface EmailVerificationService { + fun sendVerificationEmail(email: String): Boolean + fun verifyEmail(email: String, token: String): Boolean +} + +class UserService( + private val userDao: UserDao, + private val notificationService: NotificationService, + private val emailVerificationService: EmailVerificationService +) { + fun registerUser(user: User): Boolean { + if (userDao.findById(user.id) != null) { + throw IllegalArgumentException("User already exists") + } + + val isSaved = userDao.save(user.copy(isEmailVerified = false)) + if (!isSaved) { + throw IllegalStateException("User registration failed") + } + + val isVerificationEmailSent = emailVerificationService.sendVerificationEmail(user.email) + if (!isVerificationEmailSent) { + throw IllegalStateException("Failed to send verification email") + } + + return true + } + + fun verifyUserEmail(userId: Long, token: String): Boolean { + val user = userDao.findById(userId) ?: throw IllegalArgumentException("User not found") + + val isEmailVerified = emailVerificationService.verifyEmail(user.email, token) + if (!isEmailVerified) { + throw IllegalArgumentException("Email verification failed") + } + + val updatedUser = user.copy(isEmailVerified = true) + val isUpdated = userDao.update(updatedUser) + + if (isUpdated) { + notificationService.sendEmail(user.email, "Welcome ${user.name}!") + } + + return isUpdated + } +} diff --git a/src/main/kotlin/com/hibob/academy/yael/lambdaYael.kt b/src/main/kotlin/com/hibob/academy/yael/lambdaYael.kt new file mode 100644 index 000000000..bf1c289a1 --- /dev/null +++ b/src/main/kotlin/com/hibob/academy/yael/lambdaYael.kt @@ -0,0 +1,129 @@ +package com.hibob.academy.yael + +import java.time.LocalDate + +fun main13() { + //2. Initiate the variable movie in line 18 with the function createGoodMovie() + val movie = createGoodMovie() + val runner = Runner(movie) + val success = runner.init() + + printSuccessMessage(success) + println("Pretty print: ${movie.prettyPrint()}") +// println("Json: ${movie.toJson()}") +} + +//2. Initiate the variable movie in line 18 with the function createGoodMovie() +val createGoodMovie :() -> SpidermanNoWayHome = { SpidermanNoWayHome() } + +fun printSuccessMessage(success: Boolean) { + if (success) { + println("The movie was successful") + } else { + println("The movie failed") + } +} + +interface SpidermanMovieProduceActions { + fun signTobeyMaguire() + fun signAndrew() + fun signTom() + fun getVillains() + fun isThereLockdown(): Boolean + fun publish():Boolean + + val title: String + val airDate: LocalDate + val imdbRank: Double +} + +class SpidermanNoWayHome() : SpidermanMovieProduceActions { + override val title: String = "Spiderman - No Way Home" + override val airDate: LocalDate = LocalDate.of(2021,12,16) + override val imdbRank: Double = 9.6 + + //3. Implement pretty print using buildString function + fun prettyPrint(): String { + // Title: title + // Air date: airDate + // IMDB rank: rank + // use buildString function! + // return "pretty print was not implemented yet" + val builder:String = buildString { + appendLine("Title: $title") + appendLine("Air Date: $airDate") + appendLine("Imdb Rank: $imdbRank") + } + return builder + } + override fun signTobeyMaguire() { + println("Tobey signed!") + } + + override fun signAndrew() { + println("Andrew signed!") + } + + override fun signTom() { + println("Tom signed") + } + + override fun getVillains() + { + val villains = listOf( + "Green Goblin", + "Doctor Octopus", + "Sandman", + "Venom", + "The Lizard", + "Electro", + "Vulture", + "Mysterio" + ).forEach(::println) + } + + override fun isThereLockdown(): Boolean = false + + override fun publish(): Boolean = true + +// fun toJson(): JsonNode { +// /* implement the following json structure: +// { +// "title" : title, +// "airDate": 2021-12-16, +// "imdbRank": 9.6 +// } +// +// Note: In kotlin we receive the default object serializer "for free" +// and we will not have to implement it from here +// But, knowing how to write jsons in kotlin is still very important! +// the common use cases: S2S clients implementation, tests and more */ +// +// return JsonBuilderObject().json { +// "notImplemented" - true +// }.asTree() +// } + +} + +fun buildString(actions: StringBuilder.() -> Unit):String{ + val builder = StringBuilder() + builder.actions() + return builder.toString() +} + +//1. Make the Runner.init function more readable using Lambda with Receiver function usage +//4. Make SpidermanMovieProduceActions nullable (if not so yet) and make the relevant adjustments +class Runner(private val movieProducer: SpidermanMovieProduceActions?){ + fun init(): Boolean{ + return movieProducer?.run{ + if(!isThereLockdown()){ + signTobeyMaguire() + signAndrew() + signTom() + getVillains() + publish() + }else false + }?:false + } +} \ No newline at end of file diff --git a/src/test/kotlin/Types/Tamir/StoreServiceTest.kt b/src/test/kotlin/Types/Tamir/StoreServiceTest.kt new file mode 100644 index 000000000..341435856 --- /dev/null +++ b/src/test/kotlin/Types/Tamir/StoreServiceTest.kt @@ -0,0 +1,103 @@ +package com.hibob.Types.Tamir +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Test + +import java.time.LocalDate + +class StoreServiceTest { + + private val storeService = StoreService() + + private val tv = Product("1", "tv", 455.3, true) + private val ps5 = Product("2", "ps5", 3325.0, true) + private val car = Product("3", "car", 332522.0, false) + private val cart1 = Cart("1", products = listOf(tv, ps5, car)) + private val cart2 = Cart("2", products = listOf(ps5)) + private val visa = Payment.CreditCard("3555654812", LocalDate.parse("2026-03-03"), CreditCardType.VISA, 5555555.5) + private val visaIncorrectNumber = + Payment.CreditCard("35556548", LocalDate.parse("2026-03-03"), CreditCardType.VISA, 5555555.5) + private val amex = + Payment.CreditCard("3555654812", LocalDate.parse("2026-03-03"), CreditCardType.AMERICAN_EXPRESS, 5555555.5) + private val payPal = Payment.PayPal("test@com") + private val invalidPayPal = Payment.PayPal("test") + + + @Test + fun `pay method should process successfully if the data valid`() { + val res = storeService.pay(listOf(cart1, cart2), visa) + + assertThat( + res, Matchers.equalTo( + mapOf( + "1" to Check("1", Statuses.SUCCESS, 3780.3), + "2" to Check("2", Statuses.SUCCESS, 3325.0) + ) + ) + ) + } + + @Test + fun `pay method should process successfully in case pf using valid paypal`() { + val res = storeService.pay(listOf(cart1, cart2), payPal) + + assertThat( + res, Matchers.equalTo( + mapOf( + "1" to Check("1", Statuses.SUCCESS, 3780.3), + "2" to Check("2", Statuses.SUCCESS, 3325.0) + ) + ) + ) + } + + @Test + fun `pay method should failed in case of using invalid paypal`() { + val res = storeService.pay(listOf(cart1), invalidPayPal) + + assertThat( + res, Matchers.equalTo( + mapOf( + "1" to Check("1", Statuses.FAILURE, 0.0) + ) + ) + ) + } + + @Test + fun `pay method should failed in case of using cash`() { + assertThrows(IllegalStateException::class.java) { + storeService.pay(listOf(cart1), Payment.Cash) + } + } + + + @Test + fun `pay method should failed in case of incorrect visa`() { + val res = storeService.pay(listOf(cart1, cart2), visaIncorrectNumber) + + assertThat( + res, Matchers.equalTo( + mapOf( + "1" to Check("1", Statuses.FAILURE, 0.0), + "2" to Check("2", Statuses.FAILURE, 0.0) + ) + ) + ) + } + + @Test + fun `pay method should failed in case of incorrect type`() { + val res = storeService.pay(listOf(cart1, cart2), amex) + + assertThat( + res, Matchers.equalTo( + mapOf( + "1" to Check("1", Statuses.FAILURE, 0.0), + "2" to Check("2", Statuses.FAILURE, 0.0) + ) + ) + ) + } +} \ No newline at end of file diff --git a/src/test/kotlin/com/hibob/academy/dao/ExampleDaoTest.kt b/src/test/kotlin/com/hibob/academy/dao/ExampleDaoTest.kt index a02ad6bba..cac45cc5e 100644 --- a/src/test/kotlin/com/hibob/academy/dao/ExampleDaoTest.kt +++ b/src/test/kotlin/com/hibob/academy/dao/ExampleDaoTest.kt @@ -1,37 +1,37 @@ -package com.hibob.academy.dao - -import com.hibob.academy.utils.BobDbTest -import org.hamcrest.MatcherAssert.assertThat -import org.hamcrest.Matchers -import org.jooq.DSLContext -import org.junit.jupiter.api.AfterEach -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.springframework.beans.factory.annotation.Autowired -import kotlin.random.Random - -@BobDbTest -class ExampleDaoTest @Autowired constructor(private val sql: DSLContext) { - - private val dao = ExampleDao(sql) - val companyId = Random.nextLong() - val table = ExampleTable.instance - - @BeforeEach - fun createTable() { - sql.createTable(table).columns(*table.fields()).execute() - } - - @AfterEach - fun dropTable() { - sql.dropTable(table).execute() - } - - @Test - fun createAndRead() { - dao.createExample(companyId, "Hello, Kotlin!") - dao.createExample(companyId, "Hello, micro-service!") - val actual = dao.readExample(companyId) - assertThat(actual, Matchers.equalTo(Example(actual!!.id, companyId, "Hello, Kotlin!"))) - } -} \ No newline at end of file +//package com.hibob.academy.dao +// +//import com.hibob.academy.utils.BobDbTest +//import org.hamcrest.MatcherAssert.assertThat +//import org.hamcrest.Matchers +//import org.jooq.DSLContext +//import org.junit.jupiter.api.AfterEach +//import org.junit.jupiter.api.BeforeEach +//import org.junit.jupiter.api.Test +//import org.springframework.beans.factory.annotation.Autowired +//import kotlin.random.Random +// +//@BobDbTest +//class ExampleDaoTest @Autowired constructor(private val sql: DSLContext) { +// +// private val dao = ExampleDao(sql) +// val companyId = Random.nextLong() +// val table = ExampleTable.instance +// +// @BeforeEach +// fun createTable() { +// sql.createTable(table).columns(*table.fields()).execute() +// } +// +// @AfterEach +// fun dropTable() { +// sql.dropTable(table).execute() +// } +// +// @Test +// fun createAndRead() { +// dao.createExample(companyId, "Hello, Kotlin!") +// dao.createExample(companyId, "Hello, micro-service!") +// val actual = dao.readExample(companyId) +// assertThat(actual, Matchers.equalTo(Example(actual!!.id, companyId, "Hello, Kotlin!"))) +// } +//} \ No newline at end of file diff --git a/src/test/kotlin/com/hibob/academy/unitests/keren/BankAcccuntTest.kt b/src/test/kotlin/com/hibob/academy/unitests/keren/BankAcccuntTest.kt new file mode 100644 index 000000000..ca25765c3 --- /dev/null +++ b/src/test/kotlin/com/hibob/academy/unitests/keren/BankAcccuntTest.kt @@ -0,0 +1,49 @@ +import com.hibob.bootcamp.BankAccount +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Test + +/** + * Write unit tests to verify that the deposit and withdraw methods function correctly. + * Handle edge cases, such as invalid inputs (e.g., negative amounts). + * Ensure that the getBalance method returns the correct balance after a series of deposits and withdrawals. + */ + +class BankAccountTest { + + + @Test + fun `deposit valid amount increases balance`() { + assertEquals(100.0,BankAccount(0.0).deposit(100.toDouble())) + } + + @Test + fun `deposit negative or zero amount throws IllegalArgumentException`() { + assertThrows(IllegalArgumentException::class.java) { + BankAccount(0.0).deposit(0.0) + } + } + + @Test + fun `withdraw valid amount decreases balance`() { + assertEquals(0.0,BankAccount(1.0).withdraw(1.0)) + } + + @Test + fun `withdraw amount greater than balance throws IllegalArgumentException`() { + assertThrows(IllegalArgumentException::class.java) { + BankAccount(0.0).withdraw(1.0) + } + } + + @Test + fun `withdraw negative or zero amount throws IllegalArgumentException`() { + assertThrows(IllegalArgumentException::class.java) { + BankAccount(1.0).withdraw(-1.0) + } + } + + @Test + fun `getBalance returns the correct balance`() { + assertEquals(100.00,BankAccount(100.0).getBalance()) + } +} diff --git a/src/test/kotlin/com/hibob/academy/unitests/keren/ListManagerTest.kt b/src/test/kotlin/com/hibob/academy/unitests/keren/ListManagerTest.kt new file mode 100644 index 000000000..68a031db5 --- /dev/null +++ b/src/test/kotlin/com/hibob/academy/unitests/keren/ListManagerTest.kt @@ -0,0 +1,150 @@ +package com.hibob.academy.unitests.keren + +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Test + +class ListManagerTest{ + @Test + fun `Test adding a unique person`(){ + assertEquals(true,ListManager().addPerson(Person("Ron Azar", 24))) + } + + @Test + fun `Test adding a duplicate person and ensure it throws the expected exception`() { + val managers = ListManager() + managers.addPerson(Person("Ron Azar", 24)) // First insertion should not throw an exception + + // Verify that the exception is thrown when trying to add a duplicate person + assertThrows(IllegalArgumentException::class.java) { + managers.addPerson(Person("Ron Azar", 24)) // Second insertion should throw an exception + } + } + + @Test + fun `Test adding multiple people, checking that the list grows appropriately`(){ + val managers = ListManager() + managers.addPerson(Person("Ron Azar", 24)) + managers.addPerson(Person("Or Azar", 26)) + assertEquals(2,managers.getPeopleCount()) + } + + //* Write tests for removePerson method: + @Test + fun `Test removing a person that exists in the list`() { + val managers = ListManager() + val person = Person("Ron Azar", 24) + + // Add the person to the list + managers.addPerson(person) + + // Remove the person and check that the removal was successful + assertTrue(managers.removePerson(person), "The person should have been removed from the list") + + // Verify that the person is no longer in the list + assertFalse(managers.getPeopleSortedByAgeAndName().contains(person), "The person should no longer be in the list") + } + + @Test + fun `Test trying to remove a person that does not exist, ensuring it returns false`(){ + assertEquals(false,ListManager().removePerson(Person("BlahBlahBlah",120))) + } + + @Test + fun `Test the state of the list after multiple add and Need to make sure it was actually removedremove operations`(){ + val ron = Person("Ron Azar", 24) + val blah = Person("BlahBlah", 120) + val managers = ListManager() + managers.addPerson(ron) + managers.removePerson(ron) + managers.addPerson(blah) + assertTrue(managers.containManger(blah)) + assertFalse(managers.containManger(ron)) + managers.removePerson(blah) + assertEquals(0, managers.getPeopleCount()) + } + + //* Write tests for getPeopleSortedByAgeAndName method: + @Test + fun `Test with an empty list`(){ + val managers = ListManager() + val listAfterSort = managers.getPeopleSortedByAgeAndName() + assertEquals(listAfterSort.size,managers.getPeopleCount()) + } + + @Test + fun `Test with one person`(){ + val managers = ListManager() + managers.addPerson(Person("Ron Azar", 24)) + assertEquals(Person("Ron Azar", 24), managers.getPeopleSortedByAgeAndName().first()) + } + + @Test + fun `Test with multiple people to ensure they are sorted first by age, then by name`(){ + val person1 = Person("Ron Azar", 24) + val person2 = Person("BlahBlah", 120) + val person3 = Person("Gery", 24) + val managers = ListManager() + managers.addPerson(person1) + managers.addPerson(person2) + managers.addPerson(person3) + assertEquals(person1, managers.getPeopleSortedByAgeAndName()[1]) + assertEquals(person3, managers.getPeopleSortedByAgeAndName()[0]) + assertEquals(person2, managers.getPeopleSortedByAgeAndName()[2]) + } + + @Test + fun `Test with edge cases like people with the same name but different ages and vice versa`() + { + val person1 = Person("Ron Azar", 24) + val person2 = Person("Ron Azar", 120) + val person3 = Person("Gery", 24) + val managers = ListManager() + managers.addPerson(person1) + managers.addPerson(person2) + managers.addPerson(person3) + assertEquals(person1, managers.getPeopleSortedByAgeAndName()[1]) + assertEquals(person3, managers.getPeopleSortedByAgeAndName()[0]) + assertEquals(person2, managers.getPeopleSortedByAgeAndName()[2]) + } + + + //calculateStatistics + @Test + fun `Test with an empty list for calculateStatistics`() + { + assertNull(ListManager().calculateStatistics()) + } + + @Test + fun `Test with one person with legal age`(){ + val ron = Person("Ron Azar", 24) + val managers = ListManager() + managers.addPerson(ron) + assertEquals(PeopleStatistics(24.0,ron,ron, mapOf(24 to 1)),managers.calculateStatistics()) + } + + @Test + fun `Test with few person`(){ + val person1 = Person("Ron Azar", 24) + val person2 = Person("Ron Azar", 26) + val person3 = Person("Gery", 23) + val person4 = Person("Don", 25) + val managers = ListManager() + managers.addPerson(person1) + managers.addPerson(person2) + managers.addPerson(person3) + managers.addPerson(person4) + assertEquals(PeopleStatistics(24.5,person3,person2, mapOf( 24 to 1, 26 to 1, 23 to 1, 25 to 1)),managers.calculateStatistics()) + } + + @Test + fun `Test with two persons with the same age to see which one will be the younges and oldest`(){ + val person1 = Person("Ron Azar", 24) + val person2 = Person("BlahBlah", 24) + val managers = ListManager() + managers.addPerson(person1) + managers.addPerson(person2) + assertEquals(person1, managers.calculateStatistics()?.youngest) + assertEquals(person1, managers.calculateStatistics()?.oldest) + } +} \ No newline at end of file diff --git a/src/test/kotlin/com/hibob/academy/unitests/keren/UserServiceTest.kt b/src/test/kotlin/com/hibob/academy/unitests/keren/UserServiceTest.kt new file mode 100644 index 000000000..7f787cdfc --- /dev/null +++ b/src/test/kotlin/com/hibob/academy/unitests/keren/UserServiceTest.kt @@ -0,0 +1,106 @@ +package com.hibob.academy.unitests.keren + +import com.hibob.bootcamp.unittests.* +import org.hibernate.validator.internal.util.Contracts.assertTrue +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertFalse +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.mockito.kotlin.* + +class UserServiceTest{ + val token = "Waffle" + val id: Long= 123 + + private val user = User(123,"Ron Azar", "ron.azar@hibob.io" ,"12345", true) + private val userDao= mock{} + private val notificationService= mock{} + private val emailVerificationService= mock{} + private val userService=UserService(userDao,notificationService,emailVerificationService) + + @Test + fun `Test register user findById -- User already exists`(){ + whenever( userDao.findById(user.id)).thenReturn(user) + val errorMessage = assertThrows{ + userService.registerUser(user) + } + assertEquals("User already exists",errorMessage.message) + + // Verifying that save and sendVerificationEmail are NOT called because the user exists + verify(userDao, never()).save(any()) + verify(emailVerificationService, never()).sendVerificationEmail(any()) + } + + @Test + fun `Test register user findByEmail -- User registration failed`(){ + whenever( userDao.findById(user.id)).thenReturn(null) + whenever(userDao.save(user.copy(isEmailVerified = false))).thenReturn(false) + val errorMessage = assertThrows{ + userService.registerUser(user) + } + assertEquals("User registration failed",errorMessage.message) + + verify(emailVerificationService, never()).sendVerificationEmail(any()) + } + + @Test + fun `Test register user findByEmail -- Failed to send verification email`(){ + whenever( userDao.findById(user.id)).thenReturn(null) + whenever(userDao.save(user.copy(isEmailVerified = false))).thenReturn(true) + whenever(emailVerificationService.sendVerificationEmail(user.email)).thenReturn(false) + val errorMessage = assertThrows{ + userService.registerUser(user) + } + assertEquals("Failed to send verification email",errorMessage.message) + } + + @Test + fun `Test register user findByEmail -- Successfully registered user`(){ + whenever( userDao.findById(user.id)).thenReturn(null) + whenever(userDao.save(user.copy(isEmailVerified = false))).thenReturn(true) + whenever(emailVerificationService.sendVerificationEmail(user.email)).thenReturn(true) + assertTrue(userService.registerUser(user),"User not added") + } + + @Test + fun `verify User Email -- User not found`(){ + whenever( userDao.findById(id)).thenReturn(null) + val errorMessage = assertThrows{ + userService.verifyUserEmail(id,token) + } + assertEquals("User not found",errorMessage.message) + verify(emailVerificationService, never()).verifyEmail(any(), any()) + verify(userDao, never()).save(any()) + verify(userDao, never()).update(any()) + } + + @Test + fun `Test verify User Email -- Email verification failed`(){ + whenever( userDao.findById(id)).thenReturn(user) + whenever(emailVerificationService.verifyEmail(user.email, token)).thenReturn(false) + val errorMessage = assertThrows{ + userService.verifyUserEmail(id,token) + } + assertEquals("Email verification failed",errorMessage.message) + verify(userDao, never()).save(any()) + verify(userDao, never()).update(any()) + } + + @Test + fun `Test verify User Email -- isUpdated-true- send email`(){ + whenever( userDao.findById(id)).thenReturn(user) + whenever(emailVerificationService.verifyEmail(user.email, token)).thenReturn(true) + val updatedUser = user.copy() + whenever(userDao.update(updatedUser)).thenReturn(true) + assertTrue(userService.verifyUserEmail(id,token),"User updated") + } + + @Test + fun `Test verify User Email -- isUpdated-false- dont send email`(){ + whenever( userDao.findById(id)).thenReturn(user) + whenever(emailVerificationService.verifyEmail(user.email, token)).thenReturn(true) + val updatedUser = user.copy() + whenever(userDao.update(updatedUser)).thenReturn(false) + assertFalse(userService.verifyUserEmail(id,token),"User updated") + } +} \ No newline at end of file