CSC/ECE 517 Fall 2023 - E2384. Reimplement user controller.rb, user.rb and its child classes: Difference between revisions

From Expertiza_Wiki
Jump to navigation Jump to search
No edit summary
 
(135 intermediate revisions by 4 users not shown)
Line 1: Line 1:
==Expertiza==
==Description of Project==


Expertiza is a web application through which students can submit and peer-review learning objects (articles, code, web sites, etc). The National Science Foundation supports the Expertiza project.
The project aims at reimplementing user controller.rb, user.rb and it's associated child classes. The project reimplementing functionalities specific to the above classes and write extensive rspec tests for the same.  
 
It is used in select courses at NC State and by professors at several other colleges and universities.
 
 
[http://expertiza.ncsu.edu/ Expertiza] is a [http://rubyonrails.org/ Ruby on Rails] based open source project.


==Problem Statement==
==Problem Statement==


'''Background:''' Background: The [https://github.com/expertiza/expertiza/blob/main/app/models/user.rb User model] is a key component of the Expertiza application, handling user data and authentication/authorization features. It is linked to other models such as Participant, TeamsUser, and Invitation, allowing for associations and a personalized user experience.
'''Background:''' Background: The [https://github.com/expertiza/expertiza/blob/main/app/models/user.rb User model] is a key component of the Expertiza application, handling user data and authentication/authorization features. It is linked to other models such as Participant, TeamsUser, and Invitation, allowing for associations and a personalized user experience.


'''Reimplementation (What needs to be done): '''
'''Reimplementation (What needs to be done): '''
Line 18: Line 11:


* Reimplement the search_users, find_user methods in user.rb
* Reimplement the search_users, find_user methods in user.rb
* Reimplement the role, paginate_list functionality in users_controller.rb
* Reimplement the list, paginate_list functionality in users_controller.rb
* Instructor.rb
* Write thorough RSpec tests for the newly implemented functionalities
* Write thorough RSpec tests for the newly implemented functionalities


==Files Modified==
==Objectives==


We have recently made some improvements to the codebase by refactoring several methods in the application. These changes were made to enhance code readability, reduce redundancy, and promote best coding practices. In particular, we have streamlined methods related to listing, retrieving, and filtering data, resulting in cleaner and more maintainable code. These modifications aim to make our codebase more efficient while maintaining the same functionality as before.
* Reimplement methods pertaining to controller.rb, user.rb and it's associated child classes. More specifically reimplement search_users, find_user methods in user.rb.
* Reimplement the list, paginate_list functionality in users_controller.rb.
* Ensure proper naming of the methods, variables.
* Incorporate suggestions from project3.
* Ensure the PR checks are successfull and all test cases are passing.
* Compose comprehensive RSpec tests for the changes done to ensure the robustness and reliability of the entire system.


'''Changed files:'''
== Development Strategy ==
We have started the development activity using TDD approach. We start by creating test cases for the functionality in hand. Since it is reimplementation, the existing code and functionality makes it much easier to select comprehensive test cases. This is followed by writing clean and simple code to pass the test cases. We are also incorporating comments we received as part of project 3. We identified few more places where naming could be better and we will be incorporating the suggested changes in the reimplementation project.


* app/controllers/impersonate_controller.rb
==== Revised User.rb: ====
* app/controllers/users_controller.rb
* app/controllers/questionnaires_controller.rb
* app/models/instructor.rb
* app/models/ta.rb
* app/models/user.rb
* app/models/assignment_form.rb
* spec/models/ta_spec.rb
* spec/models/user_spec.rb


'''New Files:'''
# '''search_users Method:'''
## The 'search_users' method will be reimplemented to accept parameters such as 'role', 'user_id', 'letter', and 'search_by'.
## It will construct a SQL query based on the provided parameters to filter users.
## The method will return a list of users that match the search criteria.


* app/helpers/anonymized_helper.rb
# '''find_user Method:'''
## The 'find_user' method will be reimplemented to locate a user based on the provided login.
## It will first try to find the user by email. If not found, it will attempt to find the user by name.
## The method will return the found user or 'nil' if no matching user is found.


== Refactoring ==
==== Revised UsersController.rb: ====


# '''list Action:'''
## The 'list' action will be reimplemented to handle listing of users.
## It will retrieve a list of users based on certain criteria, such as name, full name or email.


The refactoring includes the following changes to improve code quality, readability, and maintainability while preserving the functionality of our software.
# '''paginate_list Action:'''
## The 'paginate_list' action will be reimplemented to handle paginated listing of users.
## It will retrieve a paginated list of users based on certain criteria, making it easier to display users in chunks.


* Recognized the need for refactoring to improve code readability and maintainability.
==== RSpec Tests: ====
* Introduced a new method, get_participants_from_instructed_entities, to handle the retrieval of participants from entities (either Course or Assignment) where the given user is an instructor.
* Replaced nested loops with more concise and efficient array operations using flat_map, reducing temporary arrays and enhancing code organization.
* Introduced a new method, filter_participants, to handle participant filtering based on user privileges. This reduced code duplication and improved readability.
* In the my_tas method, replaced the loop with a flat_map operation, extracting TA IDs directly from the TaMapping records. This simplified the code and improved efficiency.
* In the list_all, list_mine, and get methods, introduced a new method, filter_by_instructor, to handle filtering records based on the instructor ID and whether private records should be included. This reduced code duplication and improved readability.
* Throughout the refactoring process, maintained the same functionality while adding comments to explain the purpose of each method and operation.
* Used string interpolation instead of string concatenation for better readability.
* Removed redundant scopes in user.rb
* Refactored validations in user.rb
* Modified method names to reflect responsibility of the code
* Followed DRY principle and extracted repeated methods into helpers
* Added separate module for functionality pertaining to anonymized view.
* Made changes to refactor check_if_input_is_valid in impersonate_controller.rb using DRY principle to make code concise and readable.
* In user.rb renamed get_available_users method and it's function calls to get_visible_users_with_lesser_roles to make it more meaningful.
* Removed salt_first method in user_spec.rb as it does not contribute to application and is only used in test files.
* Moved functionality associated with anonymized view from user.rb to a new file app/helpers/anonymized_helper.rb.
* Replaced global variable usage of redis. Used singleton design pattern to enforce single instance of redis variable.
* Fixed lint errors


=====users_controller.rb=====
# '''User.rb Tests:'''
## RSpec tests for the 'search_users' method will ensure that the method correctly filters users based on the provided parameters.
## Tests for the 'find_user' method will validate that it successfully locates users by email or name.


# '''UsersController.rb Tests:'''
## Tests for the 'list' and 'paginate_list' action will check that it correctly paginates the list of users.


'''app/controllers/users_controller.rb'''
==== ToDo Status: ====


'''String Interpolation:''' The line flash[:note] = params[:user][:name] + ' does not exist.' has been changed to flash[:note] = "#{params[:user][:name]} does not exist.". This is a more idiomatic way to include variables in strings in Ruby.


'''Query Parameterization:''' The line @maps = ResponseMap.where('reviewee_id = ? or reviewer_id = ?', params[:id], params[:id]) has been changed to @maps = ResponseMap.where('reviewee_id = :id OR reviewer_id = :id', id: params[:id]). This is a more secure way to include parameters in SQL queries as it helps prevent SQL injection attacks.
{| class="wikitable" style="width: 100%;
!  #  ''!! Task !! Status !! Notes
|-
|1
|Reimplement <code>search_users</code> method in User.rb
|Done!
|This method will be updated to accept parameters such as `role`, `user_id`, `letter`, and `search_by`.
|-
|2
|Reimplement <code>find_user</code> method in User.rb
|Done
|This method will be updated to locate a user based on the provided login.
|-
|3
|Reimplement <code>list</code> action in UsersController.rb
|Done
|This action will be updated to handle listing of users.
|-
|4
|Reimplement <code>paginate_list</code> action in UsersController.rb
|Done
|This action will be updated to handle paginated listing of users.
|-
|5
|Write RSpec tests for User.rb
|Done
|New tests will be written for the `search_users` and `find_user` methods.
|-
|6
|Write RSpec tests for UsersController.rb
|Done
|New tests will be written for the `role` actions.
|}''


'''Method Name Changes:''' The method name foreign has been changed to available_roles. This new name is more descriptive and makes the code easier to understand.
==Test Plan==
{| class="wikitable" style="margin-left:30px"
|-
! Sr No !! Test Description
|-
| 1 || <strong>seacrh_users method in User.rb</strong>
|-
| 1.1 || Scenario 1: Test user search by name
|-
| 1.2 || Scenario 2: Test user search by email
|-
| 1.3 || Scenario 3: Test user search by fullname
|-
| 2 || <strong>find_user method in User.rb</strong>
|-
| 2.1 || Scenario 1: Test locate user by name
|-
| 2.2 || Scenario 2: Test locate user by email
|-
| 3 || <strong>list and paginate_list method in user_controller.rb</strong>
|-
| 3.1 || Scenario 1: checks that list and paginate_list does not fail with controller
|-
| 3.2 || Scenario 2: checks that list and paginate_list does not fail with post
|}


'''Removal of Redundant Code:''' The lines AssignmentParticipant.where(user_id: @user.id).each(&:delete) and TeamsUser.where(user_id: @user.id).each(&:delete) have been removed. This is because when a user is destroyed with @user.destroy, all associated records in AssignmentParticipant and TeamsUser are also automatically destroyed if the associations are set up with dependent: :destroy.
== Implementation ==


=== Create users Using rails Console ===


[[File:Controller.png]]
'''Configure Compose as a remote interpreter'''


=====instructor.rb=====
'''Connect MySQL DB and Application'''


'''app/models/instructor.rb'''
[[File:DBSV.png|800px]]




'''''list_all Method:''''' The list_all method retrieves instances of a given type where the user is either the instructor or the instance is not private. It uses an object_type.where query with an SQL condition for instructor and privacy.
Ruby syntax for creating a new user in a Rails application using the User model.


'''''list_mine Method:''''' The list_mine method retrieves instances of a given type where the user is the instructor. It uses a similar object_type.where query with an SQL condition for instructor.
User.create!(
  name: 'newuser', # lowercase name
  email: 'new_user@example.com',
  password: 'password123',
  full_name: 'New User',
  role: Role.find_by(name: 'Super Administrator')
)


'''''get Method:''''' The get method retrieves a specific instance of a given type by its ID, considering the instructor or privacy conditions. It uses the object_type.find_by method with these conditions.
=== Code ===


'''''my_tas Method:''''' The my_tas method retrieves a list of Teaching Assistant (TA) IDs associated with courses instructed by the given user. It has been refactored to use flat_map and a more efficient approach to collect TA IDs.
The search_users method accepts three parameters: user_id, key, and search_by. If a valid user_id is provided, the method returns an array containing the user with that specific user_id.
In cases where user_id is empty or invalid, the method performs a search based on the provided key and search_by parameters.
To prevent SQL injection, the method validates search_by against a predefined set of acceptable fields, including 'name', 'full_name', 'email', and 'role'. The search is then conducted using a LIKE query on the specified field, ordering the results by the user's name.


This flexible method accommodates various search scenarios, allowing users to query the system based on different criteria, enhancing the overall user experience.


[[File:instructor1.png]]
<source lang="ruby">
  def self.search_users(user_id, key, search_by)
    if user_id.present? && (user = User.find_by(id: user_id))
      # If a valid user_id is provided, return the user with that specific user_id
      return [user]
    else
      # If user_id is empty or invalid, perform the search based on key and search_by parameters


      # Validate search_by to avoid SQL injection
      valid_search_fields = %w[name full_name email role]
      search_by = valid_search_fields.include?(search_by) ? search_by : nil


'''''get_user_list Method:''''' The get_user_list method retrieves a list of users who are participants in the courses and assignments where the given user is an instructor. The code has been refactored to provide a more efficient and organized approach.
      # Perform the LIKE query on the specified field (name, full_name, email, role) and order by name
      if search_by.present?
        # Use search_by directly in the where clause
        users = User.joins(:role).where("#{search_by} LIKE ?", "%#{key}%").order(:name) if search_by == 'role'
        users ||= User.where("#{search_by} LIKE ?", "%#{key}%").order(:name)
        return users
      else
        # If search_by is not recognized, return an empty result
        return []
      end
    end
  end
</source>


'''''get_participants_from_instructed_entities Method:''''' This method is introduced to get participants from entities where the user is an instructor. It's used in get_user_list for both courses and assignments.
In the login method of authentication controller we were getting NIL error. This was because the user did not have role and institution attributes. We updated the method to allow NIL values for these attributes.


'''''filter_participants Method:''''' A new method to filter participants based on user privileges. It selects participants whose role's privileges are all included in the user's role's privileges.
<source lang="ruby">
def login
  user = User.find_by(name: params[:user_name]) || User.find_by(email: params[:user_name])
  if user&.authenticate(params[:password])
    payload = {
      id: user.id,
      name: user.name,
      full_name: user.full_name,
      role: user.role&.name,  # Use safe navigation operator (&.) to avoid nil error
      institution_id: user.institution&.id  # Use safe navigation operator (&.) to avoid nil error
    }
    token = JsonWebToken.encode(payload, 24.hours.from_now)
    render json: { token: token }, status: :ok  # Include the actual token value in the response
  else
    render json: { error: 'Invalid username/password combination' }, status: :unauthorized
  end
end
</source>


=== search_users Method ===


[[File:instructor2.png]]
This method is designed to handle user search functionality in a web application. It retrieves parameters from the request to identify the current user, search criteria, and search type. It then calls the `search_users` method from the `User` model and renders the result accordingly.


=====ta.rb=====
Users can search by `user_id`, and if it's empty or invalid, the search extends to attributes such as `name`, `full_name`, `email`, and `role`. The search results are ordered by the user's name.


'''app/models/ta.rb'''
==== Attributes of user that can be searched by====
'''''courses_assisted_with Method:''''' The courses_assisted_with method has been updated for better readability. It now directly returns an array of courses associated with the TA, using a more concise TaMapping.where and Course.find combination.


'''''instructor_or_co_ta? Method:''''' The instructor_or_co_ta? method has been enhanced for readability. It checks if the TA is either the instructor or co-TA for a given questionnaire. The code has been refactored for better structure.
* '''name:''' The name of the user.
* '''full_name:''' The full name of the user, providing a comprehensive representation.
* '''email:''' The unique email address associated with each user, ensuring distinct identification.
* '''role:''' A relationship with the [[Role Model|Role model]], defining the user's role and access privileges.


==== Result Handling ====
- If the result is a collection of users (`ActiveRecord::Relation`), it renders them as JSON.


[[File:ta1.png]]


  def search_users
    user_id = params[:user_id]
    key = params[:key]
    search_by = params[:search_by]


'''''list_all and list_mine Methods:''''' Both methods have been improved for better consistency and readability.  
    result = User.search_users(user_id, key, search_by)


''''' list_mine Method:''''' The SQL queries have been updated for a more consistent and concise format.
    if result.present?
      # If the result is not empty, render the users as JSON
      render json: result
    else
      # If the result is empty, render a message as JSON with a not found status
      render json: { error: 'User not found or no matching results' }, status: :not_found
    end
  end


This method facilitates dynamic user searches, allowing users to query the system based on different parameters. Users can search by `user_id`, and if it's empty or invalid, the search extends to attributes such as `name`, `full_name`, `email`, and `role`. The search results are ordered by the user's name.


[[File:ta2.png]]
=== find by userid method===
Method for displaying the list of users. It supports seaching user by email. If email is not present the method searches for name.


  def self.find_by_userid(userid)
    user = User.find_by(email: userid)
    if user.nil?
      items = userid.split('@')
      short_name = items[0]
      user_list = User.where('name = ?', short_name)
      user = user_list.first if user_list.any? && user_list.length == 1
    end
    user
  end


'''''get_my_instructors and get_mapped_instructor_ids Methods:''''' These methods have been simplified to use more efficient mapping and querying, making the code cleaner and more readable.
=== list and paginate_list Method ===
Method for filtering the users list with proper search and pagination.


'''''instructor and instructor= Methods:''''' These methods have been enhanced for readability. The instructor method is now a getter, and the instructor= method is a setter for better naming consistency.
This list method is used to fetch the users and display them on certain criterias which are as follows:
The search_by parameter accepts 'username', 'fullname' or 'email' as values and fetches the users based on this field. If no value for search_by is passed, all the users are displayed.
The 'letter' parameter indicates the value used to match the users based on the field obtained via the search_by parameter mentioned above.


In the paginate_list method, 'per_page' parameter indicates the number of users required to be displayed for each page.
Three options are available for this parameter, 1, 2 and 3 mapped to 25, 50 and 100 users per page respectively.
This default value for this parameter is 3, so if no value is passed for this parameter 100 users will be displayed.
If a value more than 3 is passed, all the users will be displayed.


[[File:Ta3.png]]
  def list
    # code here
    letter = params[:letter]
    search_by = params[:search_by]
    # If search parameters present
    if letter.present? && search_by.present?
      case search_by.to_i
      when 1 # Search by username
        @paginated_users = paginate_list&.where('name LIKE ?', "%#{letter}%")
      when 2 # Search by fullname
        @paginated_users = paginate_list&.where('fullname LIKE ?', "%#{letter}%")
      when 3 # Search by email
        @paginated_users = paginate_list&.where('email LIKE ?', "%#{letter}%")
      else
        @paginated_users = paginate_list
      end
    else # Display all users if no search parameters present
      @paginated_users = paginate_list
      if @paginated_users
        puts("Not empty" + @paginated_users.to_s) else puts("Empty")
      end
    end
    render json: @paginated_users
  end


'''''get_user_list Method:''''' The get_user_list method has been updated for improved efficiency and readability. It now utilizes helper methods for better organization and readability.
  def paginate_list
    paginate_options = { '1' => 25, '2' => 50, '3' => 100 }
    # If the above hash does not have a value for the key,
    # it means that we need to show all the users on the page
    #
    # Just a point to remember, when we use pagination, the
    # 'users' variable should be an object, not an array
    # The type of condition for the search depends on what the user has selected from the search_by dropdown
    @search_by = params[:search_by]
    @per_page = params[:per_page] || 3
    # search for corresponding users
    # users = User.search_users(role, user_id, letter, @search_by)
    # paginate
    users = if paginate_options[@per_page.to_s].nil? # displaying all - no pagination
              User.all
            else # some pagination is active - use the per_page
              User.paginate(page: params[:page], per_page: paginate_options[@per_page.to_s])
            end
    # users = User.all
    users
  end


'''''Helper Methods:''''' Several private helper methods have been introduced to streamline the code and improve maintainability. These methods assist in retrieving participants, filtering participants based on roles, and other tasks.
== API Documentation ==


=== Swagger API ===


[[File:Ta4.png]]
The image shows that we used Swagger to design and document our API application. Then, we exported the API data in JSON format to use Postman for development and testing. Postman is a tool that lets us send requests, check responses, write tests, and automate workflows for our API.


These changes aim to enhance the code's readability, maintainability, and efficiency while maintaining the functionality of the Ta class. The use of helper methods and better-structured SQL queries contributes to a cleaner and more organized codebase.
[[File:Swagger2.png|1024px]]


=====assignment_form.rb=====


'''app/models/assignment_form.rb'''
=== Postman Collection ===


Using update to modify multiple attributes of new_assign in a single database update operation. This is more efficient than multiple update_attribute calls.
Postman is an all-encompassing tool that enables us to send requests, validate responses, construct tests, and automate workflows for our Application Programming Interface (API). We have imported the Swagger API and created a Postman collection; this helps us to share the progress in real-time, facilitating online modifications because it is a SaaS application. In addition, we have put together a team workspace, as shown in the attached image. This arrangement fosters collaborative work and ensures the efficiency and effectiveness of our API operations.
Removed the assignment of timestamps for created_at and updated_at fields as they are already being set during the update call.


[[File:AssigForm.png]]
[[File:postman2.png|1024px]]


These changes simplify the code, reduce unnecessary database queries, and make it more efficient.
=== Endpoint: Search Users ===


=====ta_spec.rb=====
==== Description ====


'''Test Case Changes:''' The test cases have been updated to reflect these method name changes. For example, the test case for get_instructor has been changed to test instructor instead.
The endpoint search by Users can search by `user_id`, and if it's empty or invalid, the search extends to attributes such as `name`, `full_name`, `email`, and `role`. The search results are ordered by the user's name.


'''Method Call Changes:''' The way the instructor= method is called has been changed. Instead of ta.set_instructor(assignment), it’s now ta.instructor = assignment.
==== Request ====


'''Expectation Changes:''' The expectations in the test cases have been updated. For example, in the test case for instructor=, instead of expecting the method to return a value, it now checks that the instructor_id and course_id attributes of the assignment have been set correctly.
'''Method:''' GET


'''Query Changes:''' In the test case for ‘list_mine’, the SQL query string has been changed from using simple string concatenation to using a parameterized query. This can help prevent SQL injection attacks.
'''URL:''' /api/v1/users/search_users


'''Parameters:'''


'''spec/models/ta_spec.rb'''
* '''user_id (optional)''': The ID of the requesting user. If provided, it is used to check if the user is a Super Administrator.
* '''key''': The search term. This is used to perform a LIKE query on the specified field.
* '''search_by''': The field to search by. This can be 'name', 'email', 'full_name', 'id', or 'role'.


[[File:Ta_spec1.png]]
==== Swagger Endpoint ====


'''Method Name Changes:''' The method names get_instructor and set_instructor have been changed to instructor and instructor= respectively. This is more in line with Ruby’s convention for getter and setter methods.
[[File:Swagger search users.png|1024px]]


[[File:Ta_spec2.png]]


==== Response ====


=====questionnaires_controller.rb=====
'''Status Code:''' 200 OK


'''app/controllers/questionnaires_controller.rb'''
'''Body:''' A list of users that match the search term in the specified field.


==== Example ====


'''Method Name Changes:''' The method is_instructor_or_co_ta? has been changed to instructor_or_co_ta?. (methods that return a boolean value).
'''Request:'''


<pre>
GET /api/v1/users/search_users?user_id=1&key=r&search_by=role
</pre>


[[File:Questionnaires controller.png]]
'''Response:'''


<pre>
[
    {
        "id": 1,
        "name": "root",
        "full_name": "New Super Administrator",
        "email": "root@example.com",
        "email_on_review": false,
        "email_on_submission": false,
        "email_on_review_of_review": false,
        "role": {
            "id": 1,
            "name": "Super Administrator"
        },
        "parent": {
            "id": null,
            "name": null
        },
        "institution": {
            "id": null,
            "name": null
        }
    },
    {
        "id": 2,
        "name": "admin",
        "full_name": "Administrator",
        "email": "admin@example.com",
        "email_on_review": true,
        "email_on_submission": true,
        "email_on_review_of_review": true,
        "role": {
            "id": 2,
            "name": "Administrator"
        },
        "parent": {
            "id": 1,
            "name": "root"
        },
        "institution": {
            "id": 1,
            "name": "Institution 1"
        }
    },
    {
        "id": 3,
        "name": "user",
        "full_name": "Regular User",
        "email": "user@example.com",
        "email_on_review": false,
        "email_on_submission": false,
        "email_on_review_of_review": false,
        "role": {
            "id": 3,
            "name": "Instructor"
        },
        "parent": {
            "id": 2,
            "name": "admin"
        },
        "institution": {
            "id": 2,
            "name": "Institution 2"
        }
    }
]


=====impersonate_controller.rb=====
</pre>


'''app/controllers/impersonate_controller.rb'''
==== Postman Implementation ====
[[File:Postman1.jpg|800px]]


'''Method Call Changes:''' The method get_available_users has been changed to get_visible_users_with_lesser_roles. This suggests a change in the logic for fetching users, now it seems to fetch users with roles lesser than the current user.
==== Request ====


'''Code Simplification:''' The check_if_input_is_valid method has been simplified. Instead of checking separately for params[:user][:name] and params[:impersonate][:name], it now checks for a user_name that could come from either params[:user][:name] or params[:impersonate][:name]. This reduces code duplication.
'''Method:''' GET


'''Error Handling:''' The error handling in check_if_input_is_valid has been consolidated into a single line. If the user_name contains special characters, an error message is flashed and the user is redirected back.
'''URL:''' /api/v1/users/list


[[File:check_if_input_is_valid.png]]
'''Parameters:'''


=====user_spec.rb=====
* '''search_by (optional)''': The field to search by. This can be 'name', 'email', 'full_name', 'id', or 'role'.
* '''letter (optional)''': The field to indicate the starting letter of the user's 'name', 'email', or 'full_name'.
* '''per_page  (optional)''': The field to indicate how many users to be displayed per page.


'''spec/models/user_spec.rb'''
==== Response ====


'''Method Name Changes:''' The method names get_instructor and get_available_users have been changed to instructor and get_visible_users_with_lesser_roles, respectively.
'''Status Code:''' 200 OK


'''Test Case Changes:''' The test cases have been updated to reflect these method name changes. For example, the test case for get_instructor has been changed to test instructor instead, and the test case for get_available_users has been changed to test get_visible_users_with_lesser_roles.
'''Body:''' A list of users that match the search term in the specified field.


'''Expectation Changes:''' The expectations in the test cases have been updated. For example, in the test case for instructor, it now checks that the instructor method returns the correct id.
==== Example ====


[[File:User_spec1.png]]
'''Request:'''


=====anonymized_helper.rb=====
<pre>
'''app/helpers/anonymized_helper.rb'''
GET api/v1/users/list?letter=n&search_by=1&per_page=3
</pre>


'''anonymized_view?(ip_address = nil):''' This method checks if the view should be anonymized for a given IP address. It retrieves a string of starter IP addresses for anonymized views from Redis (an in-memory data structure store) and checks if the provided IP address is included in that string. If the IP address is included, it returns true, indicating that the view should be anonymized. If not, it returns false.
'''Response:'''


<pre>
[
    {
        "id": 1,
        "name": "newuser",
        "full_name": "New User",
        "email": "new_user@example.com",
        "email_on_review": false,
        "email_on_submission": false,
        "email_on_review_of_review": false,
        "role": {
            "id": 1,
            "name": "Super Administrator"
        },
        "parent": {
            "id": null,
            "name": null
        },
        "institution": {
            "id": null,
            "name": null
        }
    }
]
</pre>


[[File: anonymized_helper.png]]
==== Swagger Endpoint ====


'''real_user_from_anonymized_name(anonymized_name):''' This method retrieves the original user from an anonymized name. It finds a user in the database with a name that matches the provided anonymized name and returns that user.
[[File:Swagger-list.png|1024px]]


[[File: anonymized_helper2.png]]
==== Postman Implementation ====
[[File:List_api.png|800px]]


==Test Cases for User Controller and child classes==
== Test RSpec ==


'''Background:''' The User Controller is an essential part of our application, managing user-related functionalities. It is responsible for handling user data and managing user roles and permissions.
=== user_spec.rb===
This includes viewing, creating, and managing users and instructors, as well as assigning courses to TAs.
require 'rails_helper'


'''Test Cases:'''
RSpec.describe User, type: :model do


* '''Accessing the 'Manage Users' Tab'''
  describe '.find_by_login' do
    - Objective: Ensure that the 'Manage Users' tab is accessible.
    context 'when login is an email' do
    - Steps: Navigate to the 'Manage Users' tab and verify that the tab opens without any errors.
      let!(:user) { create(:user, name: 'testname', email: 'test@test.com') }
      it 'returns the user with the matching email' do
        result = User.find_by_login('test@test.com')
        expect(result).to eq(user)
      end


* '''Viewing the User Table as an Instructor'''
      it 'returns nil when user with email doesnt exist' do
    - Objective: Verify that an instructor can view a table of all users.
        result = User.find_by_login('test@unknown.com')
    - Steps: Log in as an instructor, navigate to the 'Manage Users' tab,
        expect(result).to eq(nil)
       and verify that a table listing all users is visible.
       end
    end


* '''Checking User Roles'''
    context 'when login is not an email' do
    - Objective: Confirm that user roles are correctly displayed.
      let!(:user) { create(:user, name: 'testname', email: 'test@test.com') }
    - Steps: In the 'Manage Users' tab, locate the column for 'Roles' and verify that each user has a
      let!(:user2) { build(:user, name: 'testname', email: 'test@test2.com') }
role assigned and it's correctly displayed.
      it 'returns the user with the matching name' do
        result = User.find_by_login('testname')
        expect(result).to eq(user)
      end
      it 'return first user with the matching name' do
        user2.save
        result = User.find_by_login('testname')
        expect(result).to eq(user)
      end
      it 'return nil when no user with matching name' do
        result = User.find_by_login('unknown')
        expect(result).to eq(nil)
      end
    end
  end


* '''Creating Users and Instructors'''
    - Objective: Test the user and instructor creation process.
    - Steps: Navigate to the 'Create User' option, fill out the necessary information
and select the role as 'User' or 'Instructor'. Submit the form and verify that the user/instructor is created successfully.


* '''Listing and Assigning Courses to a TA'''
  describe '.search_users' do
     - Objective: Ensure that courses can be listed and assigned to a TA.
    # Creating dummy objects for the test with the help of let statement
     - Steps: Log in as an instructor, navigate to the TA's profile,
     let(:role) { Role.create(name: 'Instructor', parent_id: nil, id: 2, default_page_id: nil) }
verify that a list of courses assigned to the TA is visible. Try assigning a new course to the TA
     let(:instructor) do
and verify that it's added to their list.
      Instructor.create(id: 1234, name: 'testinstructor', email: 'test@test.com', full_name: 'Test Instructor',
                        password: '123456', role_id: 2)
    end


* '''Managing User Profile Content'''
    context 'when searching by name' do
    - Objective: Check the functionality of editing user profile content.
      it 'returns users with matching names' do
    - Steps: Navigate to a user profile, verify that you can edit the content of the user profile
        # Test scenario 1
and the changes are saved correctly.
        search_result = User.search_users(nil, 'testins', 'name')
        expect(search_result).to include(instructor)
        # Test scenario 2
        search_result = User.search_users(nil, 'unknown', 'name')
        expect(search_result).to be_empty
      end
    end


* '''User Login and Logout'''
    context 'when searching by fullname' do
    - Objective: Test the user login and logout process.
      it 'returns users with matching fullnames' do
    - Steps: On the login page, enter the user's credentials and submit.
        # Test scenario 1
Verify that the user is logged in successfully. Click on the 'Logout' button and verify that the user is logged out.
        search_result = User.search_users(nil, 'Test', 'full_name')
        expect(search_result).to include(instructor)
        # Test scenario 2
        search_result = User.search_users(nil, 'UnknownName', 'full_name')
        expect(search_result).to be_empty
      end
    end


'''Note:''' These are basic test cases and might need to be adjusted based on the actual functionalities of your application. It's also a good practice to include negative test cases to ensure the system can handle errors gracefully.
    context 'when searching by email' do
b
      it 'returns users with matching emails' do
        # Test scenario 1
        search_result = User.search_users(nil, 'test@test.com', 'email')
        expect(search_result).to include(instructor)
        # Test scenario 2
        search_result = User.search_users(nil, 'unknown@test.com', 'email')
        expect(search_result).to be_empty
      end
    end


== Next Steps ==
    context 'when searching by default' do
      it 'returns users with names starting with the specified id' do
        # Test scenario 1
        search_result = User.search_users(instructor.id, nil, nil)
        expect(search_result.map(&:id)).to include(instructor.id)
        # Test scenario 2
        search_result = User.search_users(9999, nil, nil) # Use an invalid user_id
        expect(search_result).to be_empty
      end
    end
   
    context 'when searching by role' do
      it 'returns users with matching roles' do
        # Test scenario 1
        search_result = User.search_users(nil, 'admin', 'role')
        expect(search_result.map(&:id)).to include(instructor.role_id)
        # Test scenario 2
        search_result = User.search_users(nil, 'unknown', 'role')
        expect(search_result).to be_empty
      end
    end
  end
end


* We are going to continue with SuperAdministrator.rb refactoring.
=== Test Results ===
 
[[File:Expertiza_tests.png|800px]]


==Team==
==Team==
Line 280: Line 612:
* Villar, Sergio Vargas
* Villar, Sergio Vargas


==References and Relevant Links==
==References==
 
* [[Object-Oriented Design and Programming]]
#[https://expertiza.ncsu.edu/ Expertiza]
* [https://docs.google.com/document/d/14geQRUSR5q4W8RjwQdmvcGkp17Fl5vrbRMCUWJWOjg8/edit#heading=h.6i85pe7nlq5s Final Project]
#[https://docs.google.com/document/d/1dXwTNHR26eou6EJZkME8k8FhlpxCf_roMmWzclca8bA/edit OSS Projects on Expertiza]
* [https://github.com/expertiza/reimplementation-back-end/pull/60/files#diff-a05d611b9fb47bcea66e3aaf6f25dc71d8da9976c5ccc991ea5171e938605644 GITHUB PR]
#[https://github.com/expertiza/expertiza Github]
* [https://www.youtube.com/watch?v=_aCqMjRwW_U VIDEO DEMO 5min]
#[https://github.com/expertiza/expertiza/pull/2676 Pull Request]
* [https://drive.google.com/file/d/1Cc_ztHnXRlcHJvDXswpHUNe395cGNAcE/view?usp=sharing VIDEO Setup explanation 16min]
#[http://152.7.177.73:8080/ Expertiza VCL Server]

Latest revision as of 20:12, 9 December 2023

Description of Project

The project aims at reimplementing user controller.rb, user.rb and it's associated child classes. The project reimplementing functionalities specific to the above classes and write extensive rspec tests for the same.

Problem Statement

Background: Background: The User model is a key component of the Expertiza application, handling user data and authentication/authorization features. It is linked to other models such as Participant, TeamsUser, and Invitation, allowing for associations and a personalized user experience.

Reimplementation (What needs to be done): To set up the project, follow these instructions.

  • Reimplement the search_users, find_user methods in user.rb
  • Reimplement the list, paginate_list functionality in users_controller.rb
  • Write thorough RSpec tests for the newly implemented functionalities

Objectives

  • Reimplement methods pertaining to controller.rb, user.rb and it's associated child classes. More specifically reimplement search_users, find_user methods in user.rb.
  • Reimplement the list, paginate_list functionality in users_controller.rb.
  • Ensure proper naming of the methods, variables.
  • Incorporate suggestions from project3.
  • Ensure the PR checks are successfull and all test cases are passing.
  • Compose comprehensive RSpec tests for the changes done to ensure the robustness and reliability of the entire system.

Development Strategy

We have started the development activity using TDD approach. We start by creating test cases for the functionality in hand. Since it is reimplementation, the existing code and functionality makes it much easier to select comprehensive test cases. This is followed by writing clean and simple code to pass the test cases. We are also incorporating comments we received as part of project 3. We identified few more places where naming could be better and we will be incorporating the suggested changes in the reimplementation project.

Revised User.rb:

  1. search_users Method:
    1. The 'search_users' method will be reimplemented to accept parameters such as 'role', 'user_id', 'letter', and 'search_by'.
    2. It will construct a SQL query based on the provided parameters to filter users.
    3. The method will return a list of users that match the search criteria.
  1. find_user Method:
    1. The 'find_user' method will be reimplemented to locate a user based on the provided login.
    2. It will first try to find the user by email. If not found, it will attempt to find the user by name.
    3. The method will return the found user or 'nil' if no matching user is found.

Revised UsersController.rb:

  1. list Action:
    1. The 'list' action will be reimplemented to handle listing of users.
    2. It will retrieve a list of users based on certain criteria, such as name, full name or email.
  1. paginate_list Action:
    1. The 'paginate_list' action will be reimplemented to handle paginated listing of users.
    2. It will retrieve a paginated list of users based on certain criteria, making it easier to display users in chunks.

RSpec Tests:

  1. User.rb Tests:
    1. RSpec tests for the 'search_users' method will ensure that the method correctly filters users based on the provided parameters.
    2. Tests for the 'find_user' method will validate that it successfully locates users by email or name.
  1. UsersController.rb Tests:
    1. Tests for the 'list' and 'paginate_list' action will check that it correctly paginates the list of users.

ToDo Status:

 #  Task Status Notes
1 Reimplement search_users method in User.rb Done! This method will be updated to accept parameters such as `role`, `user_id`, `letter`, and `search_by`.
2 Reimplement find_user method in User.rb Done This method will be updated to locate a user based on the provided login.
3 Reimplement list action in UsersController.rb Done This action will be updated to handle listing of users.
4 Reimplement paginate_list action in UsersController.rb Done This action will be updated to handle paginated listing of users.
5 Write RSpec tests for User.rb Done New tests will be written for the `search_users` and `find_user` methods.
6 Write RSpec tests for UsersController.rb Done New tests will be written for the `role` actions.

Test Plan

Sr No Test Description
1 seacrh_users method in User.rb
1.1 Scenario 1: Test user search by name
1.2 Scenario 2: Test user search by email
1.3 Scenario 3: Test user search by fullname
2 find_user method in User.rb
2.1 Scenario 1: Test locate user by name
2.2 Scenario 2: Test locate user by email
3 list and paginate_list method in user_controller.rb
3.1 Scenario 1: checks that list and paginate_list does not fail with controller
3.2 Scenario 2: checks that list and paginate_list does not fail with post

Implementation

Create users Using rails Console

Configure Compose as a remote interpreter

Connect MySQL DB and Application


Ruby syntax for creating a new user in a Rails application using the User model.

User.create!(
 name: 'newuser', # lowercase name
 email: 'new_user@example.com',
 password: 'password123',
 full_name: 'New User',
 role: Role.find_by(name: 'Super Administrator')
)

Code

The search_users method accepts three parameters: user_id, key, and search_by. If a valid user_id is provided, the method returns an array containing the user with that specific user_id. In cases where user_id is empty or invalid, the method performs a search based on the provided key and search_by parameters. To prevent SQL injection, the method validates search_by against a predefined set of acceptable fields, including 'name', 'full_name', 'email', and 'role'. The search is then conducted using a LIKE query on the specified field, ordering the results by the user's name.

This flexible method accommodates various search scenarios, allowing users to query the system based on different criteria, enhancing the overall user experience.

  def self.search_users(user_id, key, search_by)
    if user_id.present? && (user = User.find_by(id: user_id))
      # If a valid user_id is provided, return the user with that specific user_id
      return [user]
    else
      # If user_id is empty or invalid, perform the search based on key and search_by parameters

      # Validate search_by to avoid SQL injection
      valid_search_fields = %w[name full_name email role]
      search_by = valid_search_fields.include?(search_by) ? search_by : nil

      # Perform the LIKE query on the specified field (name, full_name, email, role) and order by name
      if search_by.present?
        # Use search_by directly in the where clause
        users = User.joins(:role).where("#{search_by} LIKE ?", "%#{key}%").order(:name) if search_by == 'role'
        users ||= User.where("#{search_by} LIKE ?", "%#{key}%").order(:name)
        return users
      else
        # If search_by is not recognized, return an empty result
        return []
      end
    end
  end

In the login method of authentication controller we were getting NIL error. This was because the user did not have role and institution attributes. We updated the method to allow NIL values for these attributes.

 def login
   user = User.find_by(name: params[:user_name]) || User.find_by(email: params[:user_name])
 
   if user&.authenticate(params[:password])
     payload = {
       id: user.id,
       name: user.name,
       full_name: user.full_name,
       role: user.role&.name,  # Use safe navigation operator (&.) to avoid nil error
       institution_id: user.institution&.id  # Use safe navigation operator (&.) to avoid nil error
     }
 
     token = JsonWebToken.encode(payload, 24.hours.from_now)
     render json: { token: token }, status: :ok  # Include the actual token value in the response
   else
     render json: { error: 'Invalid username/password combination' }, status: :unauthorized
   end
 end

search_users Method

This method is designed to handle user search functionality in a web application. It retrieves parameters from the request to identify the current user, search criteria, and search type. It then calls the `search_users` method from the `User` model and renders the result accordingly.

Users can search by `user_id`, and if it's empty or invalid, the search extends to attributes such as `name`, `full_name`, `email`, and `role`. The search results are ordered by the user's name.

Attributes of user that can be searched by

  • name: The name of the user.
  • full_name: The full name of the user, providing a comprehensive representation.
  • email: The unique email address associated with each user, ensuring distinct identification.
  • role: A relationship with the Role model, defining the user's role and access privileges.

Result Handling

- If the result is a collection of users (`ActiveRecord::Relation`), it renders them as JSON.


 def search_users
   user_id = params[:user_id]
   key = params[:key]
   search_by = params[:search_by]
   result = User.search_users(user_id, key, search_by)
   if result.present?
     # If the result is not empty, render the users as JSON
     render json: result
   else
     # If the result is empty, render a message as JSON with a not found status
     render json: { error: 'User not found or no matching results' }, status: :not_found
   end
 end

This method facilitates dynamic user searches, allowing users to query the system based on different parameters. Users can search by `user_id`, and if it's empty or invalid, the search extends to attributes such as `name`, `full_name`, `email`, and `role`. The search results are ordered by the user's name.

find by userid method

Method for displaying the list of users. It supports seaching user by email. If email is not present the method searches for name.

 def self.find_by_userid(userid)
   user = User.find_by(email: userid)
   if user.nil?
     items = userid.split('@')
     short_name = items[0]
     user_list = User.where('name = ?', short_name)
     user = user_list.first if user_list.any? && user_list.length == 1
   end
   user
 end

list and paginate_list Method

Method for filtering the users list with proper search and pagination.

This list method is used to fetch the users and display them on certain criterias which are as follows: The search_by parameter accepts 'username', 'fullname' or 'email' as values and fetches the users based on this field. If no value for search_by is passed, all the users are displayed. The 'letter' parameter indicates the value used to match the users based on the field obtained via the search_by parameter mentioned above.

In the paginate_list method, 'per_page' parameter indicates the number of users required to be displayed for each page. Three options are available for this parameter, 1, 2 and 3 mapped to 25, 50 and 100 users per page respectively. This default value for this parameter is 3, so if no value is passed for this parameter 100 users will be displayed. If a value more than 3 is passed, all the users will be displayed.

 def list
   # code here
   letter = params[:letter]
   search_by = params[:search_by]
   # If search parameters present
   if letter.present? && search_by.present?
     case search_by.to_i
     when 1 # Search by username
       @paginated_users = paginate_list&.where('name LIKE ?', "%#{letter}%")
     when 2 # Search by fullname
       @paginated_users = paginate_list&.where('fullname LIKE ?', "%#{letter}%")
     when 3 # Search by email
       @paginated_users = paginate_list&.where('email LIKE ?', "%#{letter}%")
     else
       @paginated_users = paginate_list
     end
   else # Display all users if no search parameters present
     @paginated_users = paginate_list
     if @paginated_users
       puts("Not empty" + @paginated_users.to_s) else puts("Empty")
     end
   end
   render json: @paginated_users
 end
 def paginate_list
   paginate_options = { '1' => 25, '2' => 50, '3' => 100 }
   # If the above hash does not have a value for the key,
   # it means that we need to show all the users on the page
   #
   # Just a point to remember, when we use pagination, the
   # 'users' variable should be an object, not an array
   # The type of condition for the search depends on what the user has selected from the search_by dropdown
   @search_by = params[:search_by]
   @per_page = params[:per_page] || 3
   # search for corresponding users
   # users = User.search_users(role, user_id, letter, @search_by)
   # paginate
   users = if paginate_options[@per_page.to_s].nil? # displaying all - no pagination
             User.all
           else # some pagination is active - use the per_page
             User.paginate(page: params[:page], per_page: paginate_options[@per_page.to_s])
           end
   # users = User.all
   users
 end

API Documentation

Swagger API

The image shows that we used Swagger to design and document our API application. Then, we exported the API data in JSON format to use Postman for development and testing. Postman is a tool that lets us send requests, check responses, write tests, and automate workflows for our API.


Postman Collection

Postman is an all-encompassing tool that enables us to send requests, validate responses, construct tests, and automate workflows for our Application Programming Interface (API). We have imported the Swagger API and created a Postman collection; this helps us to share the progress in real-time, facilitating online modifications because it is a SaaS application. In addition, we have put together a team workspace, as shown in the attached image. This arrangement fosters collaborative work and ensures the efficiency and effectiveness of our API operations.

Endpoint: Search Users

Description

The endpoint search by Users can search by `user_id`, and if it's empty or invalid, the search extends to attributes such as `name`, `full_name`, `email`, and `role`. The search results are ordered by the user's name.

Request

Method: GET

URL: /api/v1/users/search_users

Parameters:

  • user_id (optional): The ID of the requesting user. If provided, it is used to check if the user is a Super Administrator.
  • key: The search term. This is used to perform a LIKE query on the specified field.
  • search_by: The field to search by. This can be 'name', 'email', 'full_name', 'id', or 'role'.

Swagger Endpoint


Response

Status Code: 200 OK

Body: A list of users that match the search term in the specified field.

Example

Request:

GET /api/v1/users/search_users?user_id=1&key=r&search_by=role

Response:

[
    {
        "id": 1,
        "name": "root",
        "full_name": "New Super Administrator",
        "email": "root@example.com",
        "email_on_review": false,
        "email_on_submission": false,
        "email_on_review_of_review": false,
        "role": {
            "id": 1,
            "name": "Super Administrator"
        },
        "parent": {
            "id": null,
            "name": null
        },
        "institution": {
            "id": null,
            "name": null
        }
    },
    {
        "id": 2,
        "name": "admin",
        "full_name": "Administrator",
        "email": "admin@example.com",
        "email_on_review": true,
        "email_on_submission": true,
        "email_on_review_of_review": true,
        "role": {
            "id": 2,
            "name": "Administrator"
        },
        "parent": {
            "id": 1,
            "name": "root"
        },
        "institution": {
            "id": 1,
            "name": "Institution 1"
        }
    },
    {
        "id": 3,
        "name": "user",
        "full_name": "Regular User",
        "email": "user@example.com",
        "email_on_review": false,
        "email_on_submission": false,
        "email_on_review_of_review": false,
        "role": {
            "id": 3,
            "name": "Instructor"
        },
        "parent": {
            "id": 2,
            "name": "admin"
        },
        "institution": {
            "id": 2,
            "name": "Institution 2"
        }
    }
]

Postman Implementation

Request

Method: GET

URL: /api/v1/users/list

Parameters:

  • search_by (optional): The field to search by. This can be 'name', 'email', 'full_name', 'id', or 'role'.
  • letter (optional): The field to indicate the starting letter of the user's 'name', 'email', or 'full_name'.
  • per_page (optional): The field to indicate how many users to be displayed per page.

Response

Status Code: 200 OK

Body: A list of users that match the search term in the specified field.

Example

Request:

GET api/v1/users/list?letter=n&search_by=1&per_page=3

Response:

[
    {
        "id": 1,
        "name": "newuser",
        "full_name": "New User",
        "email": "new_user@example.com",
        "email_on_review": false,
        "email_on_submission": false,
        "email_on_review_of_review": false,
        "role": {
            "id": 1,
            "name": "Super Administrator"
        },
        "parent": {
            "id": null,
            "name": null
        },
        "institution": {
            "id": null,
            "name": null
        }
    }
]

Swagger Endpoint

Postman Implementation

Test RSpec

user_spec.rb

require 'rails_helper'

RSpec.describe User, type: :model do

 describe '.find_by_login' do
   context 'when login is an email' do
     let!(:user) { create(:user, name: 'testname', email: 'test@test.com') }
     it 'returns the user with the matching email' do
       result = User.find_by_login('test@test.com')
       expect(result).to eq(user)
     end
     it 'returns nil when user with email doesnt exist' do
       result = User.find_by_login('test@unknown.com')
       expect(result).to eq(nil)
     end
   end
   context 'when login is not an email' do
     let!(:user) { create(:user, name: 'testname', email: 'test@test.com') }
     let!(:user2) { build(:user, name: 'testname', email: 'test@test2.com') }
     it 'returns the user with the matching name' do
       result = User.find_by_login('testname')
       expect(result).to eq(user)
     end
     it 'return first user with the matching name' do
       user2.save
       result = User.find_by_login('testname')
       expect(result).to eq(user)
     end
     it 'return nil when no user with matching name' do
       result = User.find_by_login('unknown')
       expect(result).to eq(nil)
     end
   end
 end


 describe '.search_users' do
   # Creating dummy objects for the test with the help of let statement
   let(:role) { Role.create(name: 'Instructor', parent_id: nil, id: 2, default_page_id: nil) }
   let(:instructor) do
     Instructor.create(id: 1234, name: 'testinstructor', email: 'test@test.com', full_name: 'Test Instructor',
                       password: '123456', role_id: 2)
   end
   context 'when searching by name' do
     it 'returns users with matching names' do
       # Test scenario 1
       search_result = User.search_users(nil, 'testins', 'name')
       expect(search_result).to include(instructor)
       # Test scenario 2
       search_result = User.search_users(nil, 'unknown', 'name')
       expect(search_result).to be_empty
     end
   end
   context 'when searching by fullname' do
     it 'returns users with matching fullnames' do
       # Test scenario 1
       search_result = User.search_users(nil, 'Test', 'full_name')
       expect(search_result).to include(instructor)
       # Test scenario 2
       search_result = User.search_users(nil, 'UnknownName', 'full_name')
       expect(search_result).to be_empty
     end
   end
   context 'when searching by email' do
     it 'returns users with matching emails' do
       # Test scenario 1
       search_result = User.search_users(nil, 'test@test.com', 'email')
       expect(search_result).to include(instructor)
       # Test scenario 2
       search_result = User.search_users(nil, 'unknown@test.com', 'email')
       expect(search_result).to be_empty
     end
   end
   context 'when searching by default' do
     it 'returns users with names starting with the specified id' do
       # Test scenario 1
       search_result = User.search_users(instructor.id, nil, nil)
       expect(search_result.map(&:id)).to include(instructor.id)
       # Test scenario 2
       search_result = User.search_users(9999, nil, nil) # Use an invalid user_id
       expect(search_result).to be_empty
     end
   end
   
   context 'when searching by role' do
     it 'returns users with matching roles' do
       # Test scenario 1
       search_result = User.search_users(nil, 'admin', 'role')
       expect(search_result.map(&:id)).to include(instructor.role_id)
       # Test scenario 2
       search_result = User.search_users(nil, 'unknown', 'role')
       expect(search_result).to be_empty
     end
   end
 end

end

Test Results

Team

Mentor
  • Devashish Vachhani
Members
  • Doddaguni, Sachin R
  • Mahesh, Amogh
  • Villar, Sergio Vargas

References