<commit_before>#
# Copyright (C) 2011 Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#

require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')

class HashWithDupCheck < Hash
  def []=(k,v)
    if self.key?(k)
      raise ArgumentError, "key already exists: #{k.inspect}"
    else
      super
    end
  end
end

# make an API call using the given method (GET/PUT/POST/DELETE),
# to the given path (e.g. /api/v1/courses). params will be verified to match the
# params generated by the Rails routing engine. body_params are params in a
# PUT/POST that are included in the body rather than the URI, and therefore
# don't affect routing.
def api_call(method, path, params, body_params = {}, headers = {}, opts = {})
  raw_api_call(method, path, params, body_params, headers, opts)
  if opts[:expected_status]
    response.status.to_i.should == opts[:expected_status]
  else
    response.should be_success, response.body
  end

  if response.headers['Link']
    # make sure that the link header is properly formed
    Api.parse_pagination_links(response.headers['Link'])
  end

  if jsonapi_call?(headers) && method == :delete
    response.status.should == '204 No Content'
    return
  end

  case params[:format]
  when 'json'
    response.header['content-type'].should == 'application/json; charset=utf-8'

    body = response.body
    if body.respond_to?(:call)
      StringIO.new.tap { |sio| body.call(nil, sio); body = sio.string }
    end
    # Check that the body doesn't have any duplicate keys. this can happen if
    # you add both a string and a symbol to the hash before calling to_json on
    # it.
    # The ruby JSON gem allows this, and it's technically valid JSON to have
    # duplicate names in an object ("names SHOULD be unique"), but it's silly
    # and we're not gonna let it slip through again.
    JSON.parse(body, :object_class => HashWithDupCheck)
  else
    raise("Don't know how to handle response format #{params[:format]}")
  end
end

def jsonapi_call?(headers)
  headers['Accept'] == 'application/vnd.api+json'
end

# like api_call, but performed by the specified user instead of @user
def api_call_as_user(user, method, path, params, body_params = {}, headers = {}, opts = {})
  token = access_token_for_user(user)
  headers['Authorization'] = "Bearer #{token}"
  account = opts[:domain_root_account] || Account.default
  user.pseudonyms.reload
  account.pseudonyms.create!(:unique_id => <EMAIL>", :user => user) unless user.find_pseudonym_for_account(account, true)
  Pseudonym.any_instance.stubs(:works_for_account?).returns(true)
  api_call(method, path, params, body_params, headers, opts)
end

$spec_api_tokens = {}

def access_token_for_user(user)
  token = $spec_api_tokens[user]
  unless token
    token = $spec_api_tokens[user] = user.access_tokens.create!(:purpose => "test").full_token
  end
  token
end

# like api_call, but don't assume success and a json response.
def raw_api_call(method, path, params, body_params = {}, headers = {}, opts = {})
  path = path.sub(%r{\Ahttps?://[^/]+}, '') # remove protocol+host
  enable_forgery_protection do
    params_from_with_nesting(method, path).should == params

    if !params.key?(:api_key) && !params.key?(:access_token) && !headers.key?('Authorization') && @user
      token = access_token_for_user(@user)
      headers['Authorization'] = "Bearer #{token}"
      account = opts[:domain_root_account] || Account.default
      Pseudonym.any_instance.stubs(:works_for_account?).returns(true)
      account.pseudonyms.create!(:unique_id => <EMAIL>", :user => @user) unless @user.all_active_pseudonyms(:reload) && @user.find_pseudonym_for_account(account, true)
    end

    LoadAccount.stubs(:default_domain_root_account).returns(opts[:domain_root_account]) if opts.has_key?(:domain_root_account)

    __send__(method, path, params.reject { |k,v| %w(controller action).include?(k.to_s) }.merge(body_params), headers)
  end
end

def follow_pagination_link(rel, params={})
  links = Api.parse_pagination_links(response.headers['Link'])
  link = links.find{ |l| l[:rel] == rel }
  link.delete(:rel)
  uri = link.delete(:uri).to_s
  link.each{ |key,value| params[key.to_sym] = value }
  api_call(:get, uri, params)
end

def params_from_with_nesting(method, path)
  path, querystring = path.split('?')
  params = ActionController::Routing::Routes.recognize_path(path, :method => method)
  querystring.blank? ? params : params.merge(Rack::Utils.parse_nested_query(querystring).symbolize_keys!)
end

def api_json_response(objects, opts = nil)
  JSON.parse(objects.to_json(opts.merge(:include_root => false)))
end

# passes the cb a piece of user content html text. the block should return the
# response from the api for that field, which will be verified for correctness.
def should_translate_user_content(course)
  attachment = attachment_model(:context => course)
  content = %{
    <p>
      Hello, students.<br>
      This will explain everything: <img id="1" src="/courses/#{course.id}/files/#{attachment.id}/preview" alt="important">
      This won't explain anything:  <img id="2" src="/courses/#{course.id}/files/#{attachment.id}/download" alt="important">
      Also, watch this awesome video: <a href="/media_objects/qwerty" class="instructure_inline_media_comment video_comment" id="media_comment_qwerty"><img></a>
      And refer to this <a href="/courses/#{course.id}/wiki/awesome-page">awesome wiki page</a>.
    </p>
  }
  html = yield content<commit_msg>fix api spec helper for rails 3<commit_after>#
# Copyright (C) 2011 Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#

require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')

unless CANVAS_RAILS2
  RSpec::configure do |c|
    c.include RSpec::Rails::RequestExampleGroup, :type => :request, :example_group => {
        :file_path => c.escaped_path(%w[spec apis])
    }
  end
end

class HashWithDupCheck < Hash
  def []=(k,v)
    if self.key?(k)
      raise ArgumentError, "key already exists: #{k.inspect}"
    else
      super
    end
  end
end

# make an API call using the given method (GET/PUT/POST/DELETE),
# to the given path (e.g. /api/v1/courses). params will be verified to match the
# params generated by the Rails routing engine. body_params are params in a
# PUT/POST that are included in the body rather than the URI, and therefore
# don't affect routing.
def api_call(method, path, params, body_params = {}, headers = {}, opts = {})
  raw_api_call(method, path, params, body_params, headers, opts)
  if opts[:expected_status]
    response.status.to_i.should == opts[:expected_status]
  else
    response.should be_success, response.body
  end

  if response.headers['Link']
    # make sure that the link header is properly formed
    Api.parse_pagination_links(response.headers['Link'])
  end

  if jsonapi_call?(headers) && method == :delete
    response.status.should == '204 No Content'
    return
  end

  case params[:format]
  when 'json'
    response.header['content-type'].should == 'application/json; charset=utf-8'

    body = response.body
    if body.respond_to?(:call)
      StringIO.new.tap { |sio| body.call(nil, sio); body = sio.string }
    end
    # Check that the body doesn't have any duplicate keys. this can happen if
    # you add both a string and a symbol to the hash before calling to_json on
    # it.
    # The ruby JSON gem allows this, and it's technically valid JSON to have
    # duplicate names in an object ("names SHOULD be unique"), but it's silly
    # and we're not gonna let it slip through again.
    JSON.parse(body, :object_class => HashWithDupCheck)
  else
    raise("Don't know how to handle response format #{params[:format]}")
  end
end

def jsonapi_call?(headers)
  headers['Accept'] == 'application/vnd.api+json'
end

# like api_call, but performed by the specified user instead of @user
def api_call_as_user(user, method, path, params, body_params = {}, headers = {}, opts = {})
  token = access_token_for_user(user)
  headers['Authorization'] = "Bearer #{token}"
  account = opts[:domain_root_account] || Account.default
  user.pseudonyms.reload
  account.pseudonyms.create!(:unique_id => <EMAIL>", :user => user) unless user.find_pseudonym_for_account(account, true)
  Pseudonym.any_instance.stubs(:works_for_account?).returns(true)
  api_call(method, path, params, body_params, headers, opts)
end

$spec_api_tokens = {}

def access_token_for_user(user)
  token = $spec_api_tokens[user]
  unless token
    token = $spec_api_tokens[user] = user.access_tokens.create!(:purpose => "test").full_token
  end
  token
end

# like api_call, but don't assume success and a json response.
def raw_api_call(method, path, params, body_params = {}, headers = {}, opts = {})
  path = path.sub(%r{\Ahttps?://[^/]+}, '') # remove protocol+host
  enable_forgery_protection do
    params_from_with_nesting(method, path).each{|k, v| params[k].to_s.should == v.to_s}

    headers['HTTP_AUTHORIZATION'] = headers['Authorization'] if headers.key?('Authorization')
    if !params.key?(:api_key) && !params.key?(:access_token) && !headers.key?('HTTP_AUTHORIZATION') && @user
      token = access_token_for_user(@user)
      headers['HTTP_AUTHORIZATION'] = "Bearer #{token}"
      account = opts[:domain_root_account] || Account.default
      Pseudonym.any_instance.stubs(:works_for_account?).returns(true)
      account.pseudonyms.create!(:unique_id => <EMAIL>", :user => @user) unless @user.all_active_pseudonyms(:reload) && @user.find_pseudonym_for_account(account, true)
    end

    LoadAccount.stubs(:default_domain_root_account).returns(opts[:domain_root_account]) if opts.has_key?(:domain_root_account)

    __send__(method, path, params.reject { |k,v| %w(controller action).include?(k.to_s) }.merge(body_params), headers)
  end
end

def follow_pagination_link(rel, params={})
  links = Api.parse_pagination_links(response.headers['Link'])
  link = links.find{ |l| l[:rel] == rel }
  link.delete(:rel)
  uri = link.delete(:uri).to_s
  link.each{ |key,value| params[key.to_sym] = value }
  api_call(:get, uri, params)
end

def params_from_with_nesting(method, path)
  path, querystring = path.split('?')
  params = CANVAS_RAILS2 ? ActionController::Routing::Routes.recognize_path(path, :method => method) :
    CanvasRails::Application.routes.recognize_path(path, :method => method)
  querystring.blank? ? params : params.merge(Rack::Utils.parse_nested_query(querystring).symbolize_keys!)
end

def api_json_response(objects, opts = nil)
  JSON.parse(objects.to_json(opts.merge(:include_root => false)))
end

# passes the cb a piece of user content html text. the block should return the
# response from the api for that field, which will be verified for correctness.
def should_translate_user_content(course)
  attachment = attachment_model(:context => course)
  content = %{
    <p>
      Hello, students.<br>
      This will explain everything: <img id="1" src="/courses/#{course.id}/files/#{attachment.id}/preview" alt="important">
      This won't explain anything:  <img id="2" src="/courses/#{course.id}/files/#{attachment.id}/download" alt="important">
      Also, watch this awesome video: <a href="/media_objects/qwerty" class="instructure_inline_media_comment video_comment" id="media_comment_qwerty"><img></a>
      And refer to this <a href="/courses/#{course.id}/wiki/awesome-page">awesome wiki page</a>.
    </p>
  }
  html = yield content
{'fraction_non_alphanumeric': 0.12438364879910928, 'fraction_numerical': 0.002147288054716081, 'mean_word_length': 4.476916376306621, 'pattern_counts': {'":': 0, '<': 31, '<?xml version=': 0, '>': 51, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3839472', 'n_tokens_mistral': 3994, 'n_tokens_neox': 3898, 'n_words': 1144}