ruby 如何在Rspec 3.10 Rails中获取测试环境的主机和端口配置

e4eetjau  于 2023-10-17  发布在  Ruby
关注(0)|答案(2)|浏览(76)

嗨,我在app/controllers/lti_controller.rb有一个控制器文件,我需要为POST #launch端点添加测试,该端点在routes.rb中定义为/lti/launch,并按预期工作。

问题

我需要以x-www-form-urlencoded的编码格式向/lti/launch发送一些数据,并使用Net::HTTP从规范文件中完成此操作。但是由于Net::HTTP需要hostport来进行操作,我现在已经给出了localhost:3000,它将用于development环境。

提问

1.如何在Rspec 3.10中访问测试环境的主机和端口,以便我可以在spec文件中传递它们?
1.有没有办法使用post '/lti/launch',req.body:x-www-form-urlencoded的数据`?并获取测试环境的主机和端口?

我的Spec文件

spec/requests/lti_spec.rb

# frozen_string_literal: true

require "rails_helper"
require 'uri'
require 'net/http'

describe LtiController, type: :request do
    before do
        # shared keys for lms access
        @oauth_consumer_key_fromlms = 'some_keys'
        @oauth_shared_secret_fromlms = 'some_secrets'
    end

    describe "#launch" do        
        before do
            # creation of assignment and required users
            @mentor = FactoryBot.create(:user)
            @group = FactoryBot.create(:group, mentor: @mentor)
            @member = FactoryBot.create(:user)
            FactoryBot.create(:group_member, user: @member, group: @group)
            @assignment = FactoryBot.create(:assignment, group: @group, lti_consumer_key: @oauth_consumer_key_fromlms, lti_shared_secret: @oauth_shared_secret_fromlms, status: "open")
        end

        def launch_uri
            launch_url = "http://0.0.0.0:3000/lti/launch"
            URI(launch_url)
        end

        let(:parameters) {
            {
                'launch_url' => launch_uri().to_s,
                'user_id' => @member.id,
                'launch_presentation_return_url' => 'http://localhost:3000/tool_return',
                'lti_version' => 'LTI-1p0',
                'lti_message_type' => 'basic-lti-launch-request',
                'resource_link_id' => '88391-e1919-bb3456',
                'lis_person_contact_email_primary' => @member.email,
                'tool_consumer_info_product_family_code' => 'moodle',
                'context_title' => 'sample Course',
                'lis_outcome_service_url' => 'http://localhost:3000/grade_passback',
                'lis_result_sourcedid' => SecureRandom.hex(10)
            } 
        }

        def consumer_data(oauth_consumer_key_fromlms, oauth_shared_secret_fromlms, parameters)
            consumer = IMS::LTI::ToolConsumer.new(oauth_consumer_key_fromlms, oauth_shared_secret_fromlms, parameters)
            allow(consumer).to receive(:to_params).and_return(parameters)
            consumer.generate_launch_data
        end

        context "lti parameters are valid" do
            it "returns success if assignment key and secret are ok and group member is present" do
                data = consumer_data(@oauth_consumer_key_fromlms, @oauth_shared_secret_fromlms, parameters)
                response = Net::HTTP.post_form(launch_uri(), data)
                expect(response.code).to eq("200")
            end
        end

        
    end
end

我的Controller文件

app/controllers/lti_controller.rb

class LtiController < ApplicationController
  skip_before_action :verify_authenticity_token, only: :launch # for lti integration
  before_action :set_group_assignment, only: %i[launch]
  before_action :set_lti_params, only: %i[launch]
  after_action :allow_iframe_lti, only: %i[launch]
  
  def launch
    session[:is_lti]=true # the lti session starting
    require 'oauth/request_proxy/action_controller_request'

    if @assignment.blank?
      # if no assignment is found
      flash[:notice] = t("lti.launch.notice_no_assignment")
      render :launch_error, status: 401
      return
    end
    
    if @group.present? # if there is a valid group based for the lti_token_key
      @provider = IMS::LTI::ToolProvider.new(
        params[:oauth_consumer_key], # lms_oauth_consumer_key
        @assignment.lti_shared_secret, # the group's lti_token
        params
      )

      if [email protected]_request?(request) # checking the lti request from the lms end
        render :launch_error, status: 401
        return
      end

      lms_lti_host = URI.join @launch_url_from_lms, '/' # identifies the domain and saves in session
      session[:lms_domain]=lms_lti_host

      @user = User.find_by(email: @email_from_lms) # find user by matching email with circuitverse and lms 

      if @user.present? # user is present in cv 
        if @user.id == @group.mentor_id # user is teacher
          sign_in(@user) # passwordless sign_in the user as the authenticity is verified via lms
          lms_auth_success_notice = t("lti.launch.notice_lms_auth_success_teacher", email_from_lms: @email_from_lms, lms_type: @lms_type, course_title_from_lms: @course_title_from_lms)
          redirect_to group_assignment_path(@group, @assignment), notice: lms_auth_success_notice # if auth_success send to group page
        else
          user_in_group = GroupMember.find_by(user_id:@user.id,group_id:@group.id) # check if the user belongs to the cv group

          if user_in_group.present? # user is member of the group
            # render the button
            flash[:notice] =  t("lti.launch.notice_students_open_in_cv")
            create_project_if_student_present() # create project with lis_result_sourced_id for the student
            render :open_incv, status: 200

          else # user is not a member of the group
            # send the user an email
            flash[:notice] = t("lti.launch.notice_ask_teacher")
            render :launch_error, status: 401
          end 
        end
      else # no such user in circuitverse,showing a notice to create an account in cv
        flash[:notice] = t("lti.launch.notice_no_account_in_cv", email_from_lms: @email_from_lms )
        render :launch_error, status: 400
      end
    else # there is no valid group present for the lti_consumer_key
      flash[:notice] = t("lti.launch.notice_invalid_group")
      render :launch_error, status: 400
    end
  end

  def allow_iframe_lti
    return unless session[:is_lti]
    
    response.headers["X-FRAME-OPTIONS"] = "ALLOW-FROM #{session[:lms_domain]}"
  end

  def create_project_if_student_present
    @user = User.find_by(email: @email_from_lms)
    @project = Project.find_by(author_id: @user.id, assignment_id: @assignment.id) # find if the project is already present
    if @project.blank? # if not then create one
      @project = @user.projects.new
      @project.name = "#{@user.name}/#{@assignment.name}"
      @project.assignment_id = @assignment.id
      @project.project_access_type = "Private"
      @project.build_project_datum
      @project.lis_result_sourced_id = params[:lis_result_sourcedid] # this param is required for grade submission
      @project.save
    end
  end

  private

    def set_group_assignment # query db and check lms_oauth_consumer_key is equal to which assignment and find the group also
      @assignment = Assignment.find_by(lti_consumer_key: params[:oauth_consumer_key])
      if @assignment.present?
        @group [email protected]
      end
    end
    
    def set_lti_params # get some of the parameters from the lti request
      @email_from_lms = params[:lis_person_contact_email_primary] # the email from the LMS
      @lms_type = params[:tool_consumer_info_product_family_code] # type of lms like moodle/canvas
      @course_title_from_lms = params[:context_title] # the course titile from lms
      @launch_url_from_lms = params[:launch_presentation_return_url]
      session[:lis_outcome_service_url] = params[:lis_outcome_service_url] # requires for grade submission
      session[:oauth_consumer_key] = params[:oauth_consumer_key] # requires for grade submission
    end
end

需要发送的样本数据

{"oauth_consumer_key"=>"some_keys", "oauth_signature_method"=>"HMAC-SHA1", "oauth_timestamp"=>"1627879512", "oauth_nonce"=>"Id3rLYZBqMvnRuSpisMEEgFkLFnkxZPS2oqoyBJZLM", "oauth_version"=>"1.0", "context_title"=>"sample Course", "launch_presentation_return_url"=>"http://localhost:3000/tool_return", "launch_url"=>"http://0.0.0.0:3000/lti/launch", "lis_outcome_service_url"=>"http://localhost:3000/grade_passback", "lis_person_contact_email_primary"=>"[email protected]", "lis_result_sourcedid"=>"3e87e3aa8f5056260a12", "lti_message_type"=>"basic-lti-launch-request", "lti_version"=>"LTI-1p0", "resource_link_id"=>"88391-e1919-bb3456", "tool_consumer_info_product_family_code"=>"moodle", "user_id"=>"461", "oauth_signature"=>"lgsHKJxHolBU1rTZ5M9zXg688hU="}
yizd12fk

yizd12fk1#

在Rspec中,您可以在发出请求之前设置主机。

it "returns success if assignment key and secret are ok and group member is present" do
  data = consumer_data(@oauth_consumer_key_fromlms, 
    @oauth_shared_secret_fromlms, parameters)
  host! "0.0.0.0:3000"
  response = Net::HTTP.post_form(launch_uri(), data)
  expect(response.code).to eq("200")
end

如果你想为所有的spec设置主机,你可以创建一个如下配置:

RSpec.configure do |config|
  config.before(:each, type: :request) do
    host! "0.0.0.0:3000"
  end
end

要使用x-www-form-urlencoded发布,您可以尝试以下操作:

url = URI.parse(launch_uri())
data = consumer_data(@oauth_consumer_key_fromlms,@oauth_shared_secret_fromlms,parameters)

request = Net::HTTP::Post.new(url.path)
request['Content-Type'] = 'application/x-www-form-urlencoded'
request.body = URI.encode_www_form(data)
response = Net::HTTP.start(url.host, url.port) do |http|
  http.request(request)
end
4ngedf3f

4ngedf3f2#

你不需要知道主机和端口。并且您不应该直接手动使用Net::HTTP执行请求。get / post /.助手们处理这个。https://relishapp.com/rspec/rspec-rails/v/5-0/docs/request-specs/request-spec

相关问题