2011-02-01 09:57:29 +08:00
#
# 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/>.
#
2013-11-27 04:43:48 +08:00
2014-07-24 01:14:22 +08:00
begin
require RUBY_VERSION > = '2.0.0' ? 'byebug' : 'debugger'
rescue LoadError
2014-01-24 05:13:49 +08:00
end
2015-04-09 01:21:08 +08:00
require 'securerandom'
2014-07-24 01:14:22 +08:00
RSpec . configure do | c |
2014-10-14 11:03:02 +08:00
c . raise_errors_for_deprecations!
2014-07-24 01:14:22 +08:00
c . color = true
c . around ( :each ) do | example |
attempts = 0
begin
Timeout :: timeout ( 180 ) {
example . run
}
if ENV [ 'AUTORERUN' ]
e = @example . instance_variable_get ( '@exception' )
if ! e . nil? && ( attempts += 1 ) < 2 && ! example . metadata [ :no_retry ]
puts " FAILURE: #{ @example . description } \n #{ e } " . red
puts " RETRYING: #{ @example . description } " . yellow
@example . instance_variable_set ( '@exception' , nil )
redo
elsif e . nil? && attempts != 0
puts " SUCCESS: retry passed for \n #{ @example . description } " . green
2014-03-28 07:29:27 +08:00
end
2014-07-24 01:14:22 +08:00
end
end until true
2014-02-27 14:04:17 +08:00
end
end
begin
; require File . expand_path ( File . dirname ( __FILE__ ) + " /../parallelized_specs/lib/parallelized_specs.rb " ) ;
rescue LoadError ;
end
2013-11-28 16:53:56 +08:00
2013-12-19 00:34:57 +08:00
ENV [ " RAILS_ENV " ] = 'test'
2013-03-21 04:30:20 +08:00
require File . expand_path ( '../../config/environment' , __FILE__ ) unless defined? ( Rails )
2014-07-24 01:14:22 +08:00
require 'rspec/rails'
2014-02-27 14:04:17 +08:00
2015-02-20 07:28:33 +08:00
Dir [ Rails . root . join ( " spec/support/**/*.rb " ) ] . each { | f | require f }
2014-07-24 01:14:22 +08:00
ActionView :: TestCase :: TestController . view_paths = ApplicationController . view_paths
2014-02-08 07:20:45 +08:00
2014-07-24 01:14:22 +08:00
module RSpec::Rails
module ViewExampleGroup
module ExampleMethods
# normally in rspec 2, assigns returns a newly constructed hash
# which means that 'assigns[:key] = value' in view specs does nothing
def assigns
@assigns || = super
2014-02-08 07:20:45 +08:00
end
2014-02-27 14:04:17 +08:00
2014-07-24 01:14:22 +08:00
alias :view_assigns :assigns
2014-02-12 21:58:05 +08:00
2014-07-24 01:14:22 +08:00
delegate :content_for , :to = > :view
2014-02-12 21:58:05 +08:00
2014-07-24 01:14:22 +08:00
def render_with_helpers ( * args )
controller_class = ( " #{ @controller . controller_path . camelize } Controller " . constantize rescue nil ) || ApplicationController
2014-02-12 21:58:05 +08:00
2014-07-24 01:14:22 +08:00
controller_class . instance_variable_set ( :@js_env , nil )
# this extends the controller's helper methods to the view
# however, these methods are delegated to the test controller
view . singleton_class . class_eval do
include controller_class . _helpers unless included_modules . include? ( controller_class . _helpers )
2014-02-12 21:58:05 +08:00
end
2014-02-27 14:04:17 +08:00
2014-07-24 01:14:22 +08:00
# so create a "real_controller"
# and delegate the helper methods to it
@controller . singleton_class . class_eval do
attr_accessor :real_controller
2014-02-12 21:58:05 +08:00
2014-07-24 01:14:22 +08:00
controller_class . _helper_methods . each do | helper |
delegate helper , :to = > :real_controller
2014-02-12 21:58:05 +08:00
end
2014-07-24 01:14:22 +08:00
end
2014-02-12 21:58:05 +08:00
2014-07-24 01:14:22 +08:00
real_controller = controller_class . new
real_controller . instance_variable_set ( :@_request , @controller . request )
@controller . real_controller = real_controller
2014-02-12 21:58:05 +08:00
2014-07-24 01:14:22 +08:00
# just calling "render 'path/to/view'" by default looks for a partial
if args . first && args . first . is_a? ( String )
file = args . shift
args = [ { :template = > file } ] + args
2014-02-12 21:58:05 +08:00
end
2014-07-24 01:14:22 +08:00
render_without_helpers ( * args )
2014-02-12 21:58:05 +08:00
end
2014-07-24 01:14:22 +08:00
alias_method_chain :render , :helpers
end
end
2014-02-12 21:58:05 +08:00
2014-07-24 01:14:22 +08:00
module Matchers
class HaveTag
include ActionDispatch :: Assertions :: SelectorAssertions
include Test :: Unit :: Assertions
2014-02-12 21:58:05 +08:00
2014-07-24 01:14:22 +08:00
def initialize ( expected )
@expected = expected
end
2014-02-12 21:58:05 +08:00
2014-07-24 01:14:22 +08:00
def matches? ( html , & block )
@selected = [ HTML :: Document . new ( html ) . root ]
assert_select ( * @expected , & block )
return ! @failed
end
2014-02-12 21:58:05 +08:00
2014-07-24 01:14:22 +08:00
def assert ( val , msg = nil )
unless ! ! val
@msg = msg
@failed = true
2014-02-12 21:58:05 +08:00
end
2014-07-24 01:14:22 +08:00
end
2014-02-12 21:58:05 +08:00
2014-10-17 03:02:40 +08:00
def failure_message
2014-07-24 01:14:22 +08:00
@msg
2014-02-12 21:58:05 +08:00
end
2014-10-17 03:02:40 +08:00
def failure_message_when_negated
2014-07-24 01:14:22 +08:00
@msg
2014-02-12 21:58:05 +08:00
end
end
2014-07-24 01:14:22 +08:00
def have_tag ( * args )
HaveTag . new ( args )
end
2014-02-12 21:58:05 +08:00
end
2013-03-22 07:32:21 +08:00
end
2014-07-24 01:14:22 +08:00
2014-01-09 04:47:34 +08:00
require 'action_controller_test_process'
2013-04-09 10:56:50 +08:00
require File . expand_path ( File . dirname ( __FILE__ ) + '/mocha_rspec_adapter' )
2013-03-15 02:51:28 +08:00
require File . expand_path ( File . dirname ( __FILE__ ) + '/mocha_extensions' )
import ActiveModel::Serializers port and convert quizzes api to it
test plan:
- The quiz api should work like it normally does when you don't pass
an 'Accept: application/vnd.api+json' header.
- The quizzes index page and quiz edit page should work like they
always do.
- Testing the Quizzes API for "jsonapi" style:
- For all requests, you MUST have the "Accept" header set to
"application/vnd.api+json"
- Test all the endpoints (PUT, POST, GET, INDEX, DELETE) like you
normally would, except you'll need to format the data according to
the next few steps:
- For "POST" and "PUT" (create and update) requests, you should send
the data like: { "quizzes": [ { id: 1, title: "blah" } ]
- For all requests (except DELETE), you should get back a response
that looks like: { "quizzes": [ { quiz you requested } ]
- For the "delete" action, you should get a "no content" response
and the request should be successful
Change-Id: Ie91deaeb6772cbe52a0fc46a28ab93a4e3036061
Reviewed-on: https://gerrit.instructure.com/25997
Reviewed-by: Jacob Fugal <jacob@instructure.com>
Tested-by: Jenkins <jenkins@instructure.com>
QA-Review: Caleb Guanzon <cguanzon@instructure.com>
Product-Review: Stanley Stuart <stanley@instructure.com>
2013-12-05 03:06:32 +08:00
require File . expand_path ( File . dirname ( __FILE__ ) + '/ams_spec_helper' )
2011-02-01 09:57:29 +08:00
2014-03-07 00:40:43 +08:00
require 'i18n_tasks'
2014-03-07 00:47:05 +08:00
require 'handlebars_tasks'
2014-03-07 00:40:43 +08:00
2014-03-15 05:19:02 +08:00
# if mocha was initialized before rails (say by another spec), CollectionProxy would have
# undef_method'd them; we need to restore them
2014-07-24 01:14:22 +08:00
Mocha :: ObjectMethods . instance_methods . each do | m |
ActiveRecord :: Associations :: CollectionProxy . class_eval <<-RUBY
def #{m}; end
remove_method #{m.inspect}
RUBY
2014-03-15 05:19:02 +08:00
end
2015-05-09 07:38:39 +08:00
factories = " #{ File . dirname ( __FILE__ ) . gsub ( / \\ / , " / " ) } /factories/*.rb "
Dir . glob ( factories ) . each { | file | require file }
examples = " #{ File . dirname ( __FILE__ ) . gsub ( / \\ / , " / " ) } /shared_examples/*.rb "
Dir . glob ( examples ) . each { | file | require file }
2011-02-01 09:57:29 +08:00
2014-01-11 01:38:35 +08:00
def pend_with_bullet
2014-02-20 05:46:50 +08:00
if defined? ( Bullet ) && Bullet . enable?
2014-10-17 21:46:48 +08:00
skip ( 'PENDING: Bullet' )
2014-01-11 01:38:35 +08:00
end
end
2014-01-04 06:09:41 +08:00
def require_webmock
# pull in webmock for selected tests, but leave it disabled by default.
# funky require order is to skip typhoeus because of an incompatibility
# see: https://github.com/typhoeus/typhoeus/issues/196
require 'webmock/util/version_checker'
require 'webmock/http_lib_adapters/http_lib_adapter_registry'
require 'webmock/http_lib_adapters/http_lib_adapter'
require 'webmock/http_lib_adapters/typhoeus_hydra_adapter'
WebMock :: HttpLibAdapterRegistry . instance . http_lib_adapters . delete :typhoeus
require 'webmock/rspec'
end
2011-02-10 01:16:29 +08:00
# rspec aliases :describe to :context in a way that it's pretty much defined
# globally on every object. :context is already heavily used in our application,
2013-03-07 04:32:49 +08:00
# so we remove rspec's definition. This does not prevent 'context' from being
# used within a 'describe' block.
2013-05-24 03:18:11 +08:00
2013-03-22 07:32:21 +08:00
if defined? ( Spec :: DSL :: Main )
module Spec::DSL::Main
remove_method :context if respond_to? :context
end
2011-02-10 01:16:29 +08:00
end
2011-06-06 23:32:11 +08:00
def truncate_table ( model )
case model . connection . adapter_name
2012-04-19 07:06:28 +08:00
when " SQLite "
model . delete_all
begin
model . connection . execute ( " delete from sqlite_sequence where name=' #{ model . connection . quote_table_name ( model . table_name ) } '; " )
model . connection . execute ( " insert into sqlite_sequence (name, seq) values (' #{ model . connection . quote_table_name ( model . table_name ) } ', #{ rand ( 100 ) } ); " )
rescue
end
2012-05-08 04:18:47 +08:00
when " PostgreSQL "
begin
old_proc = model . connection . raw_connection . set_notice_processor { }
model . connection . execute ( " TRUNCATE TABLE #{ model . connection . quote_table_name ( model . table_name ) } CASCADE " )
ensure
model . connection . raw_connection . set_notice_processor ( & old_proc )
end
2012-04-19 07:06:28 +08:00
else
2012-06-29 05:49:36 +08:00
model . connection . execute ( " SET FOREIGN_KEY_CHECKS=0 " )
2012-04-19 07:06:28 +08:00
model . connection . execute ( " TRUNCATE TABLE #{ model . connection . quote_table_name ( model . table_name ) } " )
2012-06-29 05:49:36 +08:00
model . connection . execute ( " SET FOREIGN_KEY_CHECKS=1 " )
2011-06-06 23:32:11 +08:00
end
end
2012-05-08 04:18:47 +08:00
def truncate_all_tables
2015-04-09 02:35:38 +08:00
model_connections = ActiveRecord :: Base . descendants . map ( & :connection ) . uniq
model_connections . each do | connection |
2012-05-08 04:18:47 +08:00
if connection . adapter_name == " PostgreSQL "
2015-04-09 02:35:38 +08:00
# use custom SQL to exclude tables from extensions
table_names = connection . query ( <<-SQL, 'SCHEMA').map(&:first)
SELECT tablename
FROM pg_tables
WHERE schemaname = ANY ( current_schemas ( false ) ) AND NOT tablename IN (
SELECT CAST ( objid :: regclass AS VARCHAR ) FROM pg_depend WHERE deptype = 'e'
)
SQL
2013-12-13 05:26:52 +08:00
connection . execute ( " TRUNCATE TABLE #{ table_names . map { | t | connection . quote_table_name ( t ) } . join ( ',' ) } " )
2012-05-08 04:18:47 +08:00
else
2015-04-09 02:35:38 +08:00
connection . tables . each { | model | truncate_table ( model ) }
2012-05-08 04:18:47 +08:00
end
end
end
2011-06-06 23:32:11 +08:00
# wipe out the test db, in case some non-transactional tests crapped out before
# cleaning up after themselves
2012-05-08 04:18:47 +08:00
truncate_all_tables
2011-10-01 04:22:22 +08:00
# Make AR not puke if MySQL auto-commits the transaction
2014-01-15 08:03:00 +08:00
module MysqlOutsideTransaction
2011-10-01 04:22:22 +08:00
def outside_transaction?
# MySQL ignores creation of savepoints outside of a transaction; so if we can create one
# and then can't release it because it doesn't exist, we're not in a transaction
execute ( 'SAVEPOINT outside_transaction' )
! ! execute ( 'RELEASE SAVEPOINT outside_transaction' ) rescue true
end
end
2014-01-15 08:03:00 +08:00
module ActiveRecord::ConnectionAdapters
if defined? ( MysqlAdapter )
MysqlAdapter . send ( :include , MysqlOutsideTransaction )
end
if defined? ( Mysql2Adapter )
Mysql2Adapter . send ( :include , MysqlOutsideTransaction )
end
end
2013-10-12 00:37:18 +08:00
# Be sure to actually test serializing things to non-existent caches,
# but give Mocks a pass, since they won't exist in dev/prod
Mocha :: Mock . class_eval do
def marshal_dump
nil
end
def marshal_load ( data )
raise " Mocks aren't really serializeable! "
end
2014-06-24 02:32:10 +08:00
def to_yaml ( opts = { } )
YAML . quick_emit ( self . object_id , opts ) do | out |
out . scalar ( nil , 'null' )
end
end
2013-10-12 00:37:18 +08:00
def respond_to_with_marshalling? ( symbol , include_private = false )
return true if [ :marshal_dump , :marshal_load ] . include? ( symbol )
respond_to_without_marshalling? ( symbol , include_private )
end
2013-11-27 04:43:48 +08:00
2013-10-12 00:37:18 +08:00
alias_method_chain :respond_to? , :marshalling
end
2014-07-24 01:14:22 +08:00
RSpec :: Matchers . define :encompass do | expected |
2011-12-15 04:34:57 +08:00
match do | actual |
if expected . is_a? ( Array ) && actual . is_a? ( Array )
2013-05-24 03:18:11 +08:00
expected . size == actual . size && expected . zip ( actual ) . all? { | e , a | a . slice ( * e . keys ) == e }
2011-12-15 04:34:57 +08:00
elsif expected . is_a? ( Hash ) && actual . is_a? ( Hash )
actual . slice ( * expected . keys ) == expected
else
false
end
end
end
2014-07-24 01:14:22 +08:00
RSpec :: Matchers . define :match_ignoring_whitespace do | expected |
2013-04-18 04:06:57 +08:00
def whitespaceless ( str )
str . gsub ( / \ s+ / , '' )
end
match do | actual |
whitespaceless ( actual ) == whitespaceless ( expected )
end
end
2014-01-25 02:47:43 +08:00
module Helpers
def message ( opts = { } )
m = Message . new
m . to = opts [ :to ] || 'some_user'
m . from = opts [ :from ] || 'some_other_user'
m . subject = opts [ :subject ] || 'a message for you'
m . body = opts [ :body ] || 'nice body'
m . sent_at = opts [ :sent_at ] || 5 . days . ago
m . workflow_state = opts [ :workflow_state ] || 'sent'
m . user_id = opts [ :user_id ] || opts [ :user ] . try ( :id )
m . path_type = opts [ :path_type ] || 'email'
m . root_account_id = opts [ :account_id ] || Account . default . id
m . save!
m
end
end
2014-02-06 04:56:25 +08:00
# Make sure extensions will work with dynamically created shards
if ActiveRecord :: Base . connection . adapter_name == 'PostgreSQL' &&
ActiveRecord :: Base . connection . schema_search_path == 'public'
2014-08-29 02:39:23 +08:00
Canvas . possible_postgres_extensions . each do | extension |
2014-02-06 04:56:25 +08:00
current_schema = ActiveRecord :: Base . connection . select_value ( " SELECT nspname FROM pg_extension INNER JOIN pg_namespace ON extnamespace=pg_namespace.oid WHERE extname=' #{ extension } ' " )
if current_schema && current_schema == 'public'
ActiveRecord :: Base . connection . execute ( " ALTER EXTENSION #{ extension } SET SCHEMA pg_catalog " ) rescue nil
end
end
end
2014-07-24 01:14:22 +08:00
RSpec . configure do | config |
2011-02-01 09:57:29 +08:00
# If you're not using ActiveRecord you should remove these
# lines, delete config/database.yml and disable :active_record
# in your config/boot.rb
config . use_transactional_fixtures = true
2013-05-24 03:18:11 +08:00
config . use_instantiated_fixtures = false
2013-03-08 08:08:47 +08:00
config . fixture_path = Rails . root + 'spec/fixtures/'
2014-08-14 22:55:40 +08:00
config . infer_spec_type_from_file_location!
2011-02-01 09:57:29 +08:00
2014-01-25 02:47:43 +08:00
config . include Helpers
2011-02-01 09:57:29 +08:00
2014-07-24 01:14:22 +08:00
config . include Onceler :: BasicHelpers
2014-07-23 23:15:39 +08:00
2014-07-24 01:14:22 +08:00
# rspec 2+ only runs global before(:all)'s before the top-level
# groups, not before each nested one. so we need to reset some
# things to play nicely with its caching
Onceler . configure do | c |
c . before :record do
2014-08-26 22:53:26 +08:00
Account . clear_special_account_cache! ( true )
2014-09-08 20:48:45 +08:00
Role . ensure_built_in_roles!
2014-07-24 01:14:22 +08:00
AdheresToPolicy :: Cache . clear
Folder . reset_path_lookups!
2014-06-28 16:57:40 +08:00
end
2014-07-24 01:14:22 +08:00
end
2014-06-28 16:57:40 +08:00
2014-07-24 01:14:22 +08:00
Onceler . instance_eval do
# since once-ler creates potentially multiple levels of transaction
# nesting, we need a way to know the base level so we can compare it
# to AR::Conn#open_transactions. that will tell us if something is
# "committed" or not (from the perspective of the spec)
def base_transactions
# if not recording, it's presumed we're in a spec, in which case
# transactional fixtures add one more level
open_transactions + ( recording? ? 0 : 1 )
2014-06-28 16:57:40 +08:00
end
end
2014-09-11 03:28:00 +08:00
Notification . after_create do
Notification . reset_cache!
BroadcastPolicy . notification_finder . refresh_cache
end
2012-02-07 02:36:46 +08:00
config . before :all do
# so before(:all)'s don't get confused
2014-08-26 22:53:26 +08:00
Account . clear_special_account_cache! ( true )
2014-09-08 20:48:45 +08:00
Role . ensure_built_in_roles!
2014-05-03 00:35:29 +08:00
AdheresToPolicy :: Cache . clear
2015-06-11 05:13:08 +08:00
# silence migration specs
ActiveRecord :: Migration . verbose = false
2012-02-07 02:36:46 +08:00
end
2013-11-07 07:38:56 +08:00
def delete_fixtures!
# noop for now, needed for plugin spec tweaks. implementation coming
# in g/24755
end
2014-05-17 04:05:40 +08:00
# UTC for tests, cuz it's easier :P
Account . time_zone_attribute_defaults [ :default_time_zone ] = 'UTC'
2011-02-09 04:34:00 +08:00
config . before :each do
2013-02-28 11:24:10 +08:00
I18n . locale = :en
2011-02-09 04:34:00 +08:00
Time . zone = 'UTC'
2014-08-26 22:53:26 +08:00
LoadAccount . force_special_account_reload = true
Account . clear_special_account_cache! ( true )
2014-06-28 16:57:40 +08:00
AdheresToPolicy :: Cache . clear
2011-09-02 23:34:12 +08:00
Setting . reset_cache!
2014-05-17 00:49:42 +08:00
ConfigFile . unstub
2011-11-15 06:03:57 +08:00
HostUrl . reset_cache!
2012-03-03 05:42:23 +08:00
Notification . reset_cache!
2011-12-03 07:09:07 +08:00
ActiveRecord :: Base . reset_any_instantiation!
2012-05-08 04:18:47 +08:00
Attachment . clear_cached_mime_ids
2014-07-13 15:07:19 +08:00
Folder . reset_path_lookups!
2014-09-08 20:48:45 +08:00
Role . ensure_built_in_roles!
2012-12-21 07:30:03 +08:00
RoleOverride . clear_cached_contexts
2012-08-16 23:21:18 +08:00
Delayed :: Job . redis . flushdb if Delayed :: Job == Delayed :: Backend :: Redis :: Job
2012-03-31 06:51:02 +08:00
Rails :: logger . try ( :info , " Running #{ self . class . description } #{ @method_name } " )
2013-01-08 01:57:48 +08:00
Attachment . domain_namespace = nil
2014-07-11 23:25:57 +08:00
$spec_api_tokens = { }
2011-02-09 04:34:00 +08:00
end
2011-11-04 05:51:51 +08:00
# flush redis before the first spec, and before each spec that comes after
# one that used redis
class << Canvas
attr_accessor :redis_used
2013-05-24 03:18:11 +08:00
2011-11-04 05:51:51 +08:00
def redis_with_track_usage ( * a , & b )
self . redis_used = true
redis_without_track_usage ( * a , & b )
end
2013-05-24 03:18:11 +08:00
2011-11-04 05:51:51 +08:00
alias_method_chain :redis , :track_usage
Canvas . redis_used = true
end
config . before :each do
if Canvas . redis_enabled? && Canvas . redis_used
2014-11-21 05:42:22 +08:00
Canvas . redis . flushdb
2011-11-04 05:51:51 +08:00
end
Canvas . redis_used = false
end
2011-04-08 07:01:32 +08:00
def account_with_cas ( opts = { } )
2011-10-28 02:51:15 +08:00
@account = opts [ :account ]
@account || = Account . create!
2015-03-24 23:27:55 +08:00
config = AccountAuthorizationConfig :: CAS . new
2011-04-08 07:01:32 +08:00
cas_url = opts [ :cas_url ] || " https://localhost/cas "
config . auth_type = " cas "
config . auth_base = cas_url
2011-05-16 06:27:44 +08:00
config . log_in_url = opts [ :cas_log_in_url ] if opts [ :cas_log_in_url ]
2015-06-05 00:42:53 +08:00
@account . authentication_providers << config
2011-10-28 02:51:15 +08:00
@account
2011-04-08 07:01:32 +08:00
end
2011-06-30 05:54:34 +08:00
def account_with_saml ( opts = { } )
2011-10-28 02:51:15 +08:00
@account = opts [ :account ]
@account || = Account . create!
2015-03-24 23:27:55 +08:00
config = AccountAuthorizationConfig :: SAML . new
2015-06-09 00:31:35 +08:00
config . idp_entity_id = " saml_entity "
2011-06-30 05:54:34 +08:00
config . auth_type = " saml "
config . log_in_url = opts [ :saml_log_in_url ] if opts [ :saml_log_in_url ]
refactor PseudonymSessionsController
fixes CNVS-20394
split it into appropriate concerns. main points are:
* /login never renders a login form - it redirects forward to the
default auth controller based on the first account
authorization config (or discovery url on the account)
* /login/canvas is the new home of the old login form. this form is
never rendered in-situ anymore - other places that used to render
it now redirect to /login (and then forward to here), reducing
their knowledge of SSO
* /login/ldap ends up at the same place (cause LDAP auth is handled
transparently)
* /login/cas and /login/saml redirect forward to the first SSO
configuration of the appropriate type. /login/:auth_type/:id can
be used to select a specific one
* if an SSO fails, it redirects back to /login with flash[:error]
set. this can forward to the discovery url appropriately, or
render an error page appropriately (the old no_auto=1, but now
it's not layered on top of the login partial that didn't show a
login form)
* ?canvas_login=1 is deprecated. just go directly to /login/canvas
* /saml_consume, /saml_logout are deprecated. they are processed
directly by /login/saml and /login/saml/logout
* /login/:id is deprecated - it forwards to /login/:auth_type/:id
as appropriate (presumably only saml, since that was the only
one that previously should have been using these links)
* OTP has been split into its own controller, and separated into
multiple actions instead of one all-in-one action
* /logout has been vastly simplified. the login controller should
set session[:login_aac], and on logout it will check with that
AAC for a url to redirect to after logout, instead of /login.
SSO logout is handled by each controller if they support it
test plan:
* regression test the following functionality -
* login with canvas auth
* login with LDAP auth
* login with SAML auth - and multiple SAMLs
* login with CAS auth
* MFA (configure, using, auto-setup)
* Canvas as OAuth Provider flow
* redirects to the login page when you're not
logged in
* failure of SAML/CAS (i.e. can't find user)
show a decent error page and allows retry
* "sticky" site admin auth (site admin is CAS/SAML,
going directly to another domain logs you in with
site admin)
Change-Id: I1bb9d81a101939f812cbd5020e20749e883fdc0f
Reviewed-on: https://gerrit.instructure.com/53220
QA-Review: August Thornton <august@instructure.com>
Tested-by: Jenkins
Reviewed-by: Ethan Vizitei <evizitei@instructure.com>
Product-Review: Cody Cutrer <cody@instructure.com>
2015-05-01 03:58:57 +08:00
config . log_out_url = opts [ :saml_log_out_url ] if opts [ :saml_log_out_url ]
2015-06-05 00:42:53 +08:00
@account . authentication_providers << config
2011-10-28 02:51:15 +08:00
@account
2011-06-30 05:54:34 +08:00
end
2011-02-01 09:57:29 +08:00
def course ( opts = { } )
2013-02-09 01:29:04 +08:00
account = opts [ :account ] || Account . default
account . shard . activate do
2015-03-25 07:16:40 +08:00
@course = Course . create! ( :name = > opts [ :course_name ] , :account = > account , :is_public = > ! ! opts [ :is_public ] )
2013-02-09 01:29:04 +08:00
@course . offer! if opts [ :active_course ] || opts [ :active_all ]
if opts [ :active_all ]
u = User . create!
u . register!
e = @course . enroll_teacher ( u )
e . workflow_state = 'active'
e . save!
@teacher = u
end
2014-07-04 01:55:21 +08:00
if opts [ :differentiated_assignments ]
account . allow_feature! ( :differentiated_assignments )
@course . enable_feature! ( :differentiated_assignments )
end
2015-02-19 08:07:47 +08:00
create_grading_periods_for ( @course , opts ) if opts [ :grading_periods ]
2011-02-01 09:57:29 +08:00
end
@course
end
2015-02-19 08:07:47 +08:00
def create_grading_periods_for ( context , opts = { } )
opts = { mgp_flag_enabled : true } . merge ( opts )
context . root_account = Account . default if ! context . root_account
context . root_account . enable_feature! ( :multiple_grading_periods ) if opts [ :mgp_flag_enabled ]
gp_group = context . grading_period_groups . create!
class_name = context . class . name . demodulize
2015-05-14 01:24:40 +08:00
timeframes = opts [ :grading_periods ] || [ :current ]
now = Time . zone . now
period_fixtures = {
old : {
start_date : 5 . months . ago ( now ) ,
end_date : 2 . months . ago ( now )
} ,
current : {
start_date : 2 . months . ago ( now ) ,
end_date : 2 . months . from_now ( now )
} ,
future : {
start_date : 2 . months . from_now ( now ) ,
end_date : 5 . months . from_now ( now )
2015-02-19 08:07:47 +08:00
}
2015-05-14 01:24:40 +08:00
}
timeframes . map . with_index ( 1 ) do | timeframe , index |
period_params = period_fixtures [ timeframe ] . merge ( title : " #{ class_name } Period #{ index } : #{ timeframe } period " )
gp_group . grading_periods . create! ( period_params )
2015-02-19 08:07:47 +08:00
end
end
2015-01-01 02:47:52 +08:00
def account_with_role_changes ( opts = { } )
2011-06-01 04:47:28 +08:00
account = opts [ :account ] || Account . default
if opts [ :role_changes ]
opts [ :role_changes ] . each_pair do | permission , enabled |
2014-11-13 04:13:04 +08:00
role = opts [ :role ] || admin_role
if ro = account . role_overrides . where ( :permission = > permission . to_s , :role_id = > role . id ) . first
ro . update_attribute ( :enabled , enabled )
else
account . role_overrides . create ( :permission = > permission . to_s , :enabled = > enabled , :role = > role )
end
2011-06-01 04:47:28 +08:00
end
end
2012-12-21 07:30:03 +08:00
RoleOverride . clear_cached_contexts
2015-01-01 02:47:52 +08:00
end
def account_admin_user_with_role_changes ( opts = { } )
account_with_role_changes ( opts )
2011-06-01 04:47:28 +08:00
account_admin_user ( opts )
end
2015-02-19 08:07:47 +08:00
def account_admin_user ( opts = { } )
opts = { active_user : true } . merge ( opts )
2013-02-09 01:29:04 +08:00
account = opts [ :account ] || Account . default
2015-02-19 08:07:47 +08:00
create_grading_periods_for ( account , opts ) if opts [ :grading_periods ]
2013-05-24 03:18:11 +08:00
@user = opts [ :user ] || account . shard . activate { user ( opts ) }
2011-08-12 04:50:02 +08:00
@admin = @user
2014-09-08 20:48:45 +08:00
account . account_users . create! ( :user = > @user , :role = > opts [ :role ] )
2011-05-05 07:06:29 +08:00
@user
end
2011-10-19 00:35:41 +08:00
def site_admin_user ( opts = { } )
2014-05-31 02:01:49 +08:00
account_admin_user ( opts . merge ( account : Account . site_admin ) )
2011-10-19 00:35:41 +08:00
end
2011-02-01 09:57:29 +08:00
def user ( opts = { } )
2013-03-04 15:56:39 +08:00
@user = User . create! ( opts . slice ( :name , :short_name ) )
2013-08-05 23:40:33 +08:00
if opts [ :active_user ] || opts [ :active_all ]
@user . accept_terms
@user . register!
end
2012-07-28 07:11:59 +08:00
@user . update_attribute :workflow_state , opts [ :user_state ] if opts [ :user_state ]
2011-02-01 09:57:29 +08:00
@user
end
def user_with_pseudonym ( opts = { } )
2011-12-06 04:48:11 +08:00
user ( opts ) unless opts [ :user ]
user = opts [ :user ] || @user
@pseudonym = pseudonym ( user , opts )
2011-11-24 03:52:38 +08:00
user
end
2011-12-06 04:48:11 +08:00
def communication_channel ( user , opts = { } )
2011-11-24 03:52:38 +08:00
username = opts [ :username ] || " nobody@example.com "
@cc = user . communication_channels . create! ( :path_type = > 'email' , :path = > username ) do | cc |
refactor user creation/invitations closes #5833
fixes #5573, #5572, #5753
* communication channels are now only unique within a single user
* UserList changes
* Always resolve pseudonym#unique_ids
* Support looking up by SMS CCs
* Option to either require e-mails match an existing CC,
or e-mails that don't match a Pseudonym will always be
returned unattached (relying on better merging behavior
to not have a gazillion accounts created)
* Method to return users, creating new ones (*without* a
Pseudonym) if necessary. (can't create with a pseudonym,
since Pseudonym#unique_id is still unique, I can't have
multiple outstanding users with the same unique_id)
* EnrollmentsFromUserList is mostly gutted, now using UserList's
functionality directy.
* Use UserList for adding account admins, removing the now
unused Account#add_admin => User#find_by_email/User#assert_by_email
codepath
* Update UsersController#create to not worry about duplicate
communication channels
* Remove AccountsController#add_user, and just use
UsersController#create
* Change SIS::UserImporter to send out a merge opportunity
e-mail if a conflicting CC is found (but still create the CC)
* In /profile, don't worry about conflicting CCs (the CC confirmation
process will now allow merging)
* Remove CommunicationChannelsController#try_merge and #merge
* For the non-simple case of CoursesController#enrollment_invitation
redirect to /register (CommunicationsChannelController#confirm)
* Remove CoursesController#transfer_enrollment
* Move PseudonymsController#registration_confirmation to
CommunicationChannelsController#confirm (have to be able to
register an account without a Pseudonym yet)
* Fold the old direct confirm functionality in, if there are
no available merge opportunities
* Allow merging the new account with the currently logged in user
* Allow changing the Pseudonym#unique_id when registering a new
account (since there might be conflicts)
* Display a list of merge opportunities based on conflicting
communication channels
* Provide link(s) to log in as the other user,
redirecting back to the registration page after login is
complete (to complete the merge as the current user)
* Remove several assert_* methods that are no longer needed
* Update PseudonymSessionsController a bit to deal with the new
way of dealing with conflicting CCs (especially CCs from LDAP),
and to redirect back to the registration/confirmation page when
attempting to do a merge
* Expose the open_registration setting; use it to control if
inviting users to a course is able to create new users
Change-Id: If2f38818a71af656854d3bf8431ddbf5dcb84691
Reviewed-on: https://gerrit.instructure.com/6149
Tested-by: Hudson <hudson@instructure.com>
Reviewed-by: Jacob Fugal <jacob@instructure.com>
2011-10-13 04:30:48 +08:00
cc . workflow_state = 'active' if opts [ :active_cc ] || opts [ :active_all ]
cc . workflow_state = opts [ :cc_state ] if opts [ :cc_state ]
end
2011-12-06 04:48:11 +08:00
@cc
end
def user_with_communication_channel ( opts = { } )
user ( opts ) unless opts [ :user ]
user = opts [ :user ] || @user
@cc = communication_channel ( user , opts )
user
end
def pseudonym ( user , opts = { } )
2012-02-23 03:16:29 +08:00
@spec_pseudonym_count || = 0
username = opts [ :username ] || ( @spec_pseudonym_count > 0 ? " nobody+ #{ @spec_pseudonym_count } @example.com " : " nobody@example.com " )
2012-02-15 04:26:39 +08:00
opts [ :username ] || = username
2012-02-23 03:16:29 +08:00
@spec_pseudonym_count += 1 if username =~ / nobody( \ + \ d+)?@example.com /
2011-12-06 04:48:11 +08:00
password = opts [ :password ] || " asdfasdf "
password = nil if password == :autogenerate
2015-07-04 02:57:56 +08:00
account = ( opts [ :account ] ? opts [ :account ] . root_account : Account . default )
2013-10-16 06:59:26 +08:00
@pseudonym = account . pseudonyms . build ( :user = > user , :unique_id = > username , :password = > password , :password_confirmation = > password )
@pseudonym . save_without_session_maintenance
2011-12-06 04:48:11 +08:00
@pseudonym . communication_channel = communication_channel ( user , opts )
@pseudonym
end
def managed_pseudonym ( user , opts = { } )
other_account = opts [ :account ] || account_with_saml
2015-05-28 05:20:41 +08:00
if other_account . canvas_authentication?
2015-06-05 00:42:53 +08:00
config = other_account . authentication_providers . build
2011-12-06 04:48:11 +08:00
config . auth_type = " saml "
config . log_in_url = opts [ :saml_log_in_url ] if opts [ :saml_log_in_url ]
2013-06-26 23:36:29 +08:00
config . save!
2011-12-06 04:48:11 +08:00
end
opts [ :account ] = other_account
pseudonym ( user , opts )
@pseudonym . sis_user_id = opts [ :sis_user_id ] || " U001 "
@pseudonym . save!
@pseudonym
end
def user_with_managed_pseudonym ( opts = { } )
user ( opts ) unless opts [ :user ]
user = opts [ :user ] || @user
managed_pseudonym ( user , opts )
2011-03-05 07:34:38 +08:00
user
2011-02-01 09:57:29 +08:00
end
2012-01-14 05:25:35 +08:00
def course_with_user ( enrollment_type , opts = { } )
@course = opts [ :course ] || course ( opts )
2013-05-24 03:18:11 +08:00
@user = opts [ :user ] || @course . shard . activate { user ( opts ) }
2012-04-16 23:27:28 +08:00
@enrollment = @course . enroll_user ( @user , enrollment_type , opts )
2014-02-22 02:29:35 +08:00
@user . save!
2012-01-14 05:25:35 +08:00
@enrollment . course = @course # set the reverse association
if opts [ :active_enrollment ] || opts [ :active_all ]
@enrollment . workflow_state = 'active'
@enrollment . save!
end
@course . reload
@enrollment
end
2011-02-01 09:57:29 +08:00
def course_with_student ( opts = { } )
2012-01-14 05:25:35 +08:00
course_with_user ( 'StudentEnrollment' , opts )
@student = @user
@enrollment
2011-05-20 04:40:55 +08:00
end
2012-02-14 06:26:45 +08:00
def course_with_ta ( opts = { } )
2012-03-14 04:08:19 +08:00
course_with_user ( " TaEnrollment " , opts )
2012-02-14 06:26:45 +08:00
@ta = @user
@enrollment
end
2011-05-20 04:40:55 +08:00
def course_with_student_logged_in ( opts = { } )
course_with_student ( opts )
2011-11-24 03:52:38 +08:00
user_session ( @user )
2011-05-20 04:40:55 +08:00
end
def student_in_course ( opts = { } )
2012-01-14 05:25:35 +08:00
opts [ :course ] = @course if @course && ! opts [ :course ]
course_with_student ( opts )
2011-02-01 09:57:29 +08:00
end
2012-05-10 06:45:59 +08:00
def student_in_section ( section , opts = { } )
2014-03-05 00:33:05 +08:00
student = opts . fetch ( :user ) { user }
2015-01-30 01:15:09 +08:00
enrollment = section . course . enroll_user ( student , 'StudentEnrollment' , :section = > section , :force_update = > true )
2014-03-05 00:33:05 +08:00
student . save!
2012-05-10 06:45:59 +08:00
enrollment . workflow_state = 'active'
enrollment . save!
2014-03-05 00:33:05 +08:00
student
2012-05-10 06:45:59 +08:00
end
2015-05-30 07:00:36 +08:00
def ta_in_section ( section , opts = { } )
ta = opts . fetch ( :user ) { user }
enrollment = section . course . enroll_user ( ta , 'TaEnrollment' , :section = > section , :force_update = > true )
ta . save!
enrollment . workflow_state = 'active'
enrollment . save!
ta
end
2012-03-07 07:03:03 +08:00
def teacher_in_course ( opts = { } )
2012-03-13 04:32:20 +08:00
opts [ :course ] = @course if @course && ! opts [ :course ]
course_with_teacher ( opts )
2012-03-07 07:03:03 +08:00
end
2011-02-01 09:57:29 +08:00
def course_with_teacher ( opts = { } )
2012-01-14 05:25:35 +08:00
course_with_user ( 'TeacherEnrollment' , opts )
2011-03-24 06:36:58 +08:00
@teacher = @user
2011-02-01 09:57:29 +08:00
@enrollment
end
2012-02-15 08:37:17 +08:00
def course_with_designer ( opts = { } )
course_with_user ( 'DesignerEnrollment' , opts )
@designer = @user
@enrollment
end
2011-02-01 09:57:29 +08:00
def course_with_teacher_logged_in ( opts = { } )
course_with_teacher ( opts )
user_session ( @user )
end
2012-01-14 05:25:35 +08:00
def course_with_observer ( opts = { } )
course_with_user ( 'ObserverEnrollment' , opts )
@observer = @user
@enrollment
end
def course_with_observer_logged_in ( opts = { } )
course_with_observer ( opts )
user_session ( @user )
end
2014-06-07 03:28:41 +08:00
def course_with_student_submissions ( opts = { } )
course_with_teacher_logged_in ( opts )
student_in_course
2015-02-20 00:09:01 +08:00
@course . claim! if opts [ :unpublished ]
2014-06-07 03:28:41 +08:00
submission_count = opts [ :submissions ] || 1
submission_count . times do | s |
assignment = @course . assignments . create! ( :title = > " test #{ s } assignment " )
submission = assignment . submissions . create! ( :assignment_id = > assignment . id , :user_id = > @student . id )
submission . update_attributes! ( score : '5' ) if opts [ :submission_points ]
end
end
2012-03-07 04:56:52 +08:00
def add_section ( section_name )
@course_section = @course . course_sections . create! ( :name = > section_name )
@course . reload
end
2014-12-10 00:31:04 +08:00
def multiple_student_enrollment ( user , section , opts = { } )
course = opts [ :course ] || @course || course ( opts )
@enrollment = course . enroll_student ( user ,
2012-03-14 04:08:19 +08:00
:enrollment_state = > " active " ,
:section = > section ,
:allow_multiple_enrollments = > true )
end
def enter_student_view ( opts = { } )
course = opts [ :course ] || @course || course ( opts )
@fake_student = course . student_view_student
post " /users/ #{ @fake_student . id } /masquerade "
2014-10-17 03:02:40 +08:00
expect ( session [ :become_user_id ] ) . to eq @fake_student . id . to_s
2012-03-07 04:56:52 +08:00
end
2013-11-27 08:03:47 +08:00
def account_notification ( opts = { } )
req_service = opts [ :required_account_service ] || nil
2014-09-08 20:48:45 +08:00
role_ids = opts [ :role_ids ] || [ ]
2013-11-27 08:03:47 +08:00
message = opts [ :message ] || " hi there "
2014-07-10 03:16:18 +08:00
subj = opts [ :subject ] || " this is a subject "
2013-11-27 08:03:47 +08:00
@account = opts [ :account ] || Account . default
2014-07-10 03:16:18 +08:00
@announcement = @account . announcements . build ( subject : subj , message : message , required_account_service : req_service )
2014-01-18 03:02:40 +08:00
@announcement . start_at = opts [ :start_at ] || 5 . minutes . ago . utc
2014-07-10 03:16:18 +08:00
@announcement . end_at = opts [ :end_at ] || 1 . day . from_now . utc
2014-09-08 20:48:45 +08:00
@announcement . account_notification_roles . build ( role_ids . map { | r_id | { account_notification_id : @announcement . id , role : Role . get_role_by_id ( r_id ) } } ) unless role_ids . empty?
2013-11-27 08:03:47 +08:00
@announcement . save!
2014-09-08 20:48:45 +08:00
@announcement
2013-11-27 08:03:47 +08:00
end
2012-06-09 03:39:34 +08:00
VALID_GROUP_ATTRIBUTES = [ :name , :context , :max_membership , :group_category , :join_level , :description , :is_public , :avatar_attachment ]
2013-05-24 03:18:11 +08:00
2011-02-01 09:57:29 +08:00
def group ( opts = { } )
2014-01-22 00:41:51 +08:00
context = opts [ :group_context ] || opts [ :context ] || Account . default
2013-08-08 06:19:48 +08:00
@group = context . groups . create! opts . slice ( * VALID_GROUP_ATTRIBUTES )
2011-02-01 09:57:29 +08:00
end
def group_with_user ( opts = { } )
group ( opts )
2012-06-09 03:39:34 +08:00
u = opts [ :user ] || user ( opts )
workflow_state = opts [ :active_all ] ? 'accepted' : nil
@group . add_user ( u , workflow_state , opts [ :moderator ] )
2011-05-26 00:38:32 +08:00
end
def group_with_user_logged_in ( opts = { } )
group_with_user ( opts )
user_session ( @user )
2011-02-01 09:57:29 +08:00
end
2013-07-19 06:15:40 +08:00
def group_category ( opts = { } )
context = opts [ :context ] || @course
@group_category = context . group_categories . create! ( name : opts [ :name ] || 'foo' )
end
2012-12-13 23:24:21 +08:00
def custom_role ( base , name , opts = { } )
account = opts [ :account ] || @account
2014-09-27 21:46:36 +08:00
role = account . roles . where ( name : name ) . first_or_initialize
2012-12-13 23:24:21 +08:00
role . base_role_type = base
role . save!
role
end
2013-05-24 03:18:11 +08:00
2012-12-13 23:24:21 +08:00
def custom_student_role ( name , opts = { } )
custom_role ( 'StudentEnrollment' , name , opts )
end
2013-05-24 03:18:11 +08:00
2012-12-13 23:24:21 +08:00
def custom_teacher_role ( name , opts = { } )
custom_role ( 'TeacherEnrollment' , name , opts )
end
2013-05-24 03:18:11 +08:00
2012-12-13 23:24:21 +08:00
def custom_ta_role ( name , opts = { } )
custom_role ( 'TaEnrollment' , name , opts )
end
2013-05-24 03:18:11 +08:00
2012-12-13 23:24:21 +08:00
def custom_designer_role ( name , opts = { } )
custom_role ( 'DesignerEnrollment' , name , opts )
end
2013-05-24 03:18:11 +08:00
2012-12-13 23:24:21 +08:00
def custom_observer_role ( name , opts = { } )
custom_role ( 'ObserverEnrollment' , name , opts )
end
2013-05-24 03:18:11 +08:00
2012-12-13 23:24:21 +08:00
def custom_account_role ( name , opts = { } )
2014-11-05 00:51:02 +08:00
custom_role ( Role :: DEFAULT_ACCOUNT_TYPE , name , opts )
2014-09-08 20:48:45 +08:00
end
def student_role
Role . get_built_in_role ( " StudentEnrollment " )
end
def teacher_role
Role . get_built_in_role ( " TeacherEnrollment " )
end
def ta_role
Role . get_built_in_role ( " TaEnrollment " )
end
def designer_role
Role . get_built_in_role ( " DesignerEnrollment " )
end
def observer_role
Role . get_built_in_role ( " ObserverEnrollment " )
end
def admin_role
Role . get_built_in_role ( " AccountAdmin " )
2012-12-13 23:24:21 +08:00
end
2011-02-01 09:57:29 +08:00
def user_session ( user , pseudonym = nil )
2012-05-20 04:53:25 +08:00
unless pseudonym
2014-05-07 04:38:56 +08:00
pseudonym = stub ( 'Pseudonym' , :record = > user , :user_id = > user . id , :user = > user , :login_count = > 1 )
2012-05-20 04:53:25 +08:00
# at least one thing cares about the id of the pseudonym... using the
# object_id should make it unique (but obviously things will fail if
# it tries to load it from the db.)
pseudonym . stubs ( :id ) . returns ( pseudonym . object_id )
2015-03-14 04:07:54 +08:00
pseudonym . stubs ( :unique_id ) . returns ( 'unique_id' )
2012-05-20 04:53:25 +08:00
end
2015-03-05 00:18:39 +08:00
session = stub ( 'PseudonymSession' , :record = > pseudonym , :session_credentials = > nil )
2012-05-20 04:53:25 +08:00
2011-10-26 07:15:30 +08:00
PseudonymSession . stubs ( :find ) . returns ( session )
2011-02-01 09:57:29 +08:00
end
2013-05-24 23:59:41 +08:00
def remove_user_session
PseudonymSession . unstub ( :find )
end
2011-08-25 03:40:15 +08:00
def login_as ( username = " nobody@example.com " , password = " asdfasdf " )
post_via_redirect " /login " ,
2012-04-19 07:06:28 +08:00
" pseudonym_session[unique_id] " = > username ,
" pseudonym_session[password] " = > password
2011-08-25 03:40:15 +08:00
assert_response :success
2014-10-17 03:02:40 +08:00
expect ( request . fullpath ) . to eq " /?login_success=1 "
2011-08-25 03:40:15 +08:00
end
2012-04-27 06:00:52 +08:00
def assignment_quiz ( questions , opts = { } )
2012-04-11 03:04:18 +08:00
course = opts [ :course ] || course ( :active_course = > true )
user = opts [ :user ] || user ( :active_user = > true )
2013-05-24 03:18:11 +08:00
course . enroll_student ( user , :enrollment_state = > 'active' ) unless user . enrollments . any? { | e | e . course_id == course . id }
2012-04-11 03:04:18 +08:00
@assignment = course . assignments . create ( :title = > " Test Assignment " )
2013-05-21 03:39:36 +08:00
@assignment . workflow_state = " published "
2011-11-01 00:58:44 +08:00
@assignment . submission_types = " online_quiz "
@assignment . save
2014-09-27 21:46:36 +08:00
@quiz = Quizzes :: Quiz . where ( assignment_id : @assignment ) . first
2011-11-01 00:58:44 +08:00
@questions = questions . map { | q | @quiz . quiz_questions . create! ( q ) }
@quiz . generate_quiz_data
2012-05-09 01:15:24 +08:00
@quiz . published_at = Time . now
2012-03-01 04:45:13 +08:00
@quiz . workflow_state = " available "
2012-02-21 01:51:20 +08:00
@quiz . save!
2012-04-27 06:00:52 +08:00
end
# The block should return the submission_data. A block is used so
# that we have access to the @questions variable that is created
# in this method
def quiz_with_graded_submission ( questions , opts = { } , & block )
assignment_quiz ( questions , opts )
2012-05-02 01:29:28 +08:00
@quiz_submission = @quiz . generate_submission ( @user )
2011-11-01 00:58:44 +08:00
@quiz_submission . mark_completed
@quiz_submission . submission_data = yield if block_given?
2014-04-22 04:14:52 +08:00
Quizzes :: SubmissionGrader . new ( @quiz_submission ) . grade_submission
2011-11-01 00:58:44 +08:00
end
2012-02-25 04:33:29 +08:00
def survey_with_submission ( questions , & block )
course_with_student ( :active_all = > true )
@assignment = @course . assignments . create ( :title = > " Test Assignment " )
2013-05-21 03:39:36 +08:00
@assignment . workflow_state = " published "
2012-02-25 04:33:29 +08:00
@assignment . submission_types = " online_quiz "
@assignment . save
2014-09-27 21:46:36 +08:00
@quiz = Quizzes :: Quiz . where ( assignment_id : @assignment ) . first
2012-02-25 04:33:29 +08:00
@quiz . anonymous_submissions = true
@quiz . quiz_type = " graded_survey "
@questions = questions . map { | q | @quiz . quiz_questions . create! ( q ) }
@quiz . generate_quiz_data
@quiz . save!
@quiz_submission = @quiz . generate_submission ( @user )
@quiz_submission . mark_completed
@quiz_submission . submission_data = yield if block_given?
end
2012-06-21 02:58:03 +08:00
def group_discussion_assignment
course = @course || course ( :active_all = > true )
group_category = course . group_categories . create! ( :name = > " category " )
@group1 = course . groups . create! ( :name = > " group 1 " , :group_category = > group_category )
@group2 = course . groups . create! ( :name = > " group 2 " , :group_category = > group_category )
@topic = course . discussion_topics . build ( :title = > " topic " )
2014-04-11 06:39:45 +08:00
@topic . group_category = group_category
@assignment = course . assignments . build ( :submission_types = > 'discussion_topic' , :title = > @topic . title )
make fancy midnight work for assignment overrides
also fixes an issue where some dates display as "Friday at 11:59pm" instead
of just "Friday"
Also does a little bit of refactoring and spec backfilling for the
override list presenter. The override list presenter now returns a much
more friendly list of "due date" hashes to the outside world to make it
easier to consume in views. Views don't have to format the dates by
passing in a hash anymore.
test plan:
- specs should pass
- as a teacher, create an assignment with overrides using the web
form. In one of the overrides, enter a day like March 1 at 12am.
- save the overrides
- Make sure fancy midnight works for lock dates and due dates, but not
unlock dates (12:00 am unlock date should show up as 12:00 am, not
11:59 pm)
- on the assignment's show page, you should just see "Friday", meaning
that the assignment is due at 11:59 pm on March 1.
- The "fancy midnight" scheme should work correctly for
assignments,quizzes,and discussion topics, including the default due
dates.
- Be sure to check that the dates show up correctly on the
assignment,quiz, and discussion show pages.
- Be sure to make an override that has a blank due_at, lock_at, and
unlock_at, but has a default due date, lock date, and unlock date.
The overrides should not inherit from the default due date (fixes
CNVS-4216)
fixes CNVS-4216, CNVS-4004, CNVS-3890
Change-Id: I8b5e10c074eb2a237a1298cb7def0cb32d3dcb7f
Reviewed-on: https://gerrit.instructure.com/18142
QA-Review: Amber Taniuchi <amber@instructure.com>
Tested-by: Jenkins <jenkins@instructure.com>
Reviewed-by: Simon Williams <simon@instructure.com>
2013-03-06 00:04:59 +08:00
@assignment . infer_times
2012-06-21 02:58:03 +08:00
@assignment . saved_by = :discussion_topic
@topic . assignment = @assignment
@topic . save!
end
2012-01-05 07:42:22 +08:00
def rubric_for_course
@rubric = Rubric . new ( :title = > 'My Rubric' , :context = > @course )
@rubric . data = [
2012-04-19 07:06:28 +08:00
{
2012-01-05 07:42:22 +08:00
:points = > 3 ,
2012-04-19 07:06:28 +08:00
:description = > " First row " ,
:long_description = > " The first row in the rubric " ,
:id = > 1 ,
:ratings = > [
{
:points = > 3 ,
:description = > " Rockin' " ,
:criterion_id = > 1 ,
:id = > 2
} ,
{
:points = > 2 ,
:description = > " Rockin' " ,
:criterion_id = > 1 ,
:id = > 3
} ,
{
:points = > 0 ,
:description = > " Lame " ,
:criterion_id = > 1 ,
:id = > 4
}
]
}
2012-01-05 07:42:22 +08:00
]
@rubric . save!
end
2011-08-04 23:48:19 +08:00
def outcome_with_rubric ( opts = { } )
learning outcomes refactor
This list is *NOT* complete, some items may have snuck in that I forgot
to note, and/or some of the noted items may not be completely functional
yet.
Specs need to be written around a lot of this, other specs will no doubt
need to be fixed.
Some things, particularly around LearningOutcomeGroups will need data
migrations that aren't there yet.
* remove LearningOutcome.non_rubric_outcomes? and replace with false
where invoked
* remove LearningOutcome.enabled? and replace with true where invoked
* remove never-taken branches
* remove the shared/aligned_outcomes partial and it's supporting
javascript, since it's now empty
* remove js handler for add_outcome_alignment_link and supporting
method since it only occurred in never-taken branches
* mix LearningOutcomeContext into Course and Account
* replace LearningOutcomeGroup.default_for(context) with
LearningOutcomeContext#root_outcome_group
* rename LearningOutcome#content_tags to LearningOutcome#alignments
* rename LearningOutcomeGroup#content_tags to
LearningOutcomeGroup#child_links, and properly restrict
* remove ContentTag[Alignment]#rubric_association_id, add
ContentTag[Alignment]#has_rubric_association? that looks at the
presence of the content's rubric_association_id
* condition off the assignment having a rubric_association rather than
filtering tags by has_rubric_association (which just looks back at
the assignment). all or none of the assignment's alignments are
forced to have the association (via the assignment). this was true in
practice before, is now codified (and more efficient)
* rename AssessmentQuestionBank#learning_outcome_tags to
AssessmentQuestionBank#learning_outcome_alignments
* rename Assignment#learning_outcome_tags to
Assignment#learning_outcome_alignments
* rename Rubric#learning_outcome_tags to
Rubric#learning_outcome_alignments
* move/rename (Course|Account)#learning_outcome_tags to
LearningOutcomeContext#learning_outcome_links
* move/rename Account#learning_outcomes (corrected) and
Course#learning_outcomes to
LearningOutcomeContext#linked_learning_outcomes
* move/rename Account#created_learning_outcomes and
Course#created_learning_outcomes to
LearningOutcomeContext#created_learning_outcomes
* clarify and correct usage of linked_learning_outcomes vs.
created_learning_outcomes
* move/rename (Account|Account)#learning_outcome_groups to
LearningOutcomeContext#learning_outcome_groups
* remove unused Account#associated_learning_outcomes
* just remove one link to a learning outcome when deleting
* merge Account#has_outcomes?, Course#has_outcomes? and
Course#has_outcomes into LearningOutcomeContext#has_outcomes?, add a
use in Context#active_record_types
* kill LearningOutcomeGroup#root_learning_outcome_group (unused)
* rename LearningOutcomeResult#content_tag to
LearningOutcomeResult#alignment
* kill unused (and broken) OutcomesController#add_outcome_group
* kill unused OutcomesController#update_outcomes_for_asset
* kill unused OutcomesController#outcomes_for_asset
* remove unused (outside specs, correct specs)
AssessmentQuestionBank#outcomes=
* remove unused ContentTag#learning_outcome_content
* replace ContentTag.learning_outcome_tags_for(asset) (only ever called
with asset=an assignment) with call to
Assignment#learning_outcome_alignments
* remove unused ContentTag.not_rubric
* remove (now) unused ContentTag.include_outcome
* remove unused LearningOutcome#learning_outcome_group_associations
* avoid explicit use of ContentTag in outcome-related specs
* replace LearningOutcomeGroup#learning_outcome_tags with
LearningOutcomeGroup#child_outcome_links (and only use for outcome
links; not tags for child groups)
* split ContentTag#create_outcome_result into
Submission#create_outcome_result,
QuizSubmission#create_outcome_result, and
RubricAssessment#create_outcome_result. fix some bugs along the way
* refactor ContentTag.outcome_tags_for_banks and some code from
QuizSubmission#(track_outcomes|update_outcomes_for_assessment_questions)
into QuizSubmission#questions_and_alignments
* refactor RubricAssociation#update_outcome_relations and
Rubric#update_alignments into LearningOutcome.update_alignments
* don't use ContentTag#rubric_association with outcome alignments; use
the tag's content's rubric_association in its place (they should have
been equal anyways)
* refactor LearningOutcome.available_in_context and
@context.root_outcome_group.sorted_all_outcomes (only time
sorted_all_outcomes is used) into
LearningOutcomeContext#available_outcomes and
LearningOutcomeContext#available_outcome
* overhaul LearningOutcomeGroup#sorted_content and rename to
LearningOutcomeGroup#sorted_children. it not returns ContentTags
(outcome links) and LearningOutcomeGroups, vs. LearningOutcomes and
LearningOutcomeGroups; fix usages appropriately
* fix UI for arranging/deleting outcome links and groups within a group
to refer to the outcome link rather than the outcome
Change-Id: I85d99f2634f7206332cb1f5d5ea575b428988d4b
Reviewed-on: https://gerrit.instructure.com/12590
Reviewed-by: Jacob Fugal <jacob@instructure.com>
Tested-by: Jacob Fugal <jacob@instructure.com>
2012-07-13 01:16:13 +08:00
@outcome_group || = @course . root_outcome_group
2014-12-09 00:54:00 +08:00
@outcome = @course . created_learning_outcomes . create! (
:description = > '<p>This is <b>awesome</b>.</p>' ,
:short_description = > 'new outcome' ,
:calculation_method = > 'highest'
)
learning outcomes refactor
This list is *NOT* complete, some items may have snuck in that I forgot
to note, and/or some of the noted items may not be completely functional
yet.
Specs need to be written around a lot of this, other specs will no doubt
need to be fixed.
Some things, particularly around LearningOutcomeGroups will need data
migrations that aren't there yet.
* remove LearningOutcome.non_rubric_outcomes? and replace with false
where invoked
* remove LearningOutcome.enabled? and replace with true where invoked
* remove never-taken branches
* remove the shared/aligned_outcomes partial and it's supporting
javascript, since it's now empty
* remove js handler for add_outcome_alignment_link and supporting
method since it only occurred in never-taken branches
* mix LearningOutcomeContext into Course and Account
* replace LearningOutcomeGroup.default_for(context) with
LearningOutcomeContext#root_outcome_group
* rename LearningOutcome#content_tags to LearningOutcome#alignments
* rename LearningOutcomeGroup#content_tags to
LearningOutcomeGroup#child_links, and properly restrict
* remove ContentTag[Alignment]#rubric_association_id, add
ContentTag[Alignment]#has_rubric_association? that looks at the
presence of the content's rubric_association_id
* condition off the assignment having a rubric_association rather than
filtering tags by has_rubric_association (which just looks back at
the assignment). all or none of the assignment's alignments are
forced to have the association (via the assignment). this was true in
practice before, is now codified (and more efficient)
* rename AssessmentQuestionBank#learning_outcome_tags to
AssessmentQuestionBank#learning_outcome_alignments
* rename Assignment#learning_outcome_tags to
Assignment#learning_outcome_alignments
* rename Rubric#learning_outcome_tags to
Rubric#learning_outcome_alignments
* move/rename (Course|Account)#learning_outcome_tags to
LearningOutcomeContext#learning_outcome_links
* move/rename Account#learning_outcomes (corrected) and
Course#learning_outcomes to
LearningOutcomeContext#linked_learning_outcomes
* move/rename Account#created_learning_outcomes and
Course#created_learning_outcomes to
LearningOutcomeContext#created_learning_outcomes
* clarify and correct usage of linked_learning_outcomes vs.
created_learning_outcomes
* move/rename (Account|Account)#learning_outcome_groups to
LearningOutcomeContext#learning_outcome_groups
* remove unused Account#associated_learning_outcomes
* just remove one link to a learning outcome when deleting
* merge Account#has_outcomes?, Course#has_outcomes? and
Course#has_outcomes into LearningOutcomeContext#has_outcomes?, add a
use in Context#active_record_types
* kill LearningOutcomeGroup#root_learning_outcome_group (unused)
* rename LearningOutcomeResult#content_tag to
LearningOutcomeResult#alignment
* kill unused (and broken) OutcomesController#add_outcome_group
* kill unused OutcomesController#update_outcomes_for_asset
* kill unused OutcomesController#outcomes_for_asset
* remove unused (outside specs, correct specs)
AssessmentQuestionBank#outcomes=
* remove unused ContentTag#learning_outcome_content
* replace ContentTag.learning_outcome_tags_for(asset) (only ever called
with asset=an assignment) with call to
Assignment#learning_outcome_alignments
* remove unused ContentTag.not_rubric
* remove (now) unused ContentTag.include_outcome
* remove unused LearningOutcome#learning_outcome_group_associations
* avoid explicit use of ContentTag in outcome-related specs
* replace LearningOutcomeGroup#learning_outcome_tags with
LearningOutcomeGroup#child_outcome_links (and only use for outcome
links; not tags for child groups)
* split ContentTag#create_outcome_result into
Submission#create_outcome_result,
QuizSubmission#create_outcome_result, and
RubricAssessment#create_outcome_result. fix some bugs along the way
* refactor ContentTag.outcome_tags_for_banks and some code from
QuizSubmission#(track_outcomes|update_outcomes_for_assessment_questions)
into QuizSubmission#questions_and_alignments
* refactor RubricAssociation#update_outcome_relations and
Rubric#update_alignments into LearningOutcome.update_alignments
* don't use ContentTag#rubric_association with outcome alignments; use
the tag's content's rubric_association in its place (they should have
been equal anyways)
* refactor LearningOutcome.available_in_context and
@context.root_outcome_group.sorted_all_outcomes (only time
sorted_all_outcomes is used) into
LearningOutcomeContext#available_outcomes and
LearningOutcomeContext#available_outcome
* overhaul LearningOutcomeGroup#sorted_content and rename to
LearningOutcomeGroup#sorted_children. it not returns ContentTags
(outcome links) and LearningOutcomeGroups, vs. LearningOutcomes and
LearningOutcomeGroups; fix usages appropriately
* fix UI for arranging/deleting outcome links and groups within a group
to refer to the outcome link rather than the outcome
Change-Id: I85d99f2634f7206332cb1f5d5ea575b428988d4b
Reviewed-on: https://gerrit.instructure.com/12590
Reviewed-by: Jacob Fugal <jacob@instructure.com>
Tested-by: Jacob Fugal <jacob@instructure.com>
2012-07-13 01:16:13 +08:00
@outcome_group . add_outcome ( @outcome )
2011-11-24 03:52:38 +08:00
@outcome_group . save!
2011-08-11 23:13:00 +08:00
correctly maintain assignment/rubric <-> outcome links
There are a few old LearningOutcomeResults that have a nil artifact, and
many that have a Submission as an artifact. We want to get rid of these
because they come from bad data, and step 1 toward that goal is to stop
creating them and hide them in the UI.
The LORs with a nil artifact are very old and I believe came from a very
early incarnation of outcomes. The LORs with a Submission as an artifact
came from a combination of two code problems. The first was old code
that allowed an assignment that was aligned with an outcome but did not
have a rubric to create LORs directly based on it's submission. The
second was a bug that prevented the assignment <-> outcome link from
being destroyed when a rubric with an outcome was removed from an
assignment.
fixes CNVS-7495
fixes CNVS-7498
test plan:
- try different combinations of adding a rubric with an outcome to an
assignment.
- when you grade the assignment, the grade create a learning outcome result
(which can be seen on the outcome show page, or in the account outcome
report) if the rubric+outcome are currently attached to the assignment.
- so for example, add a rubric with an outcome, check, remove just the outcome
row, check, add a new outcome row, check, remove the whole rubric, check.
- be sure to check both the show page and the outcome report
TODO:
- datafix migration
Change-Id: I37700e3e5c08fc6cfb8fcf1cac42ea6693fcaba3
Reviewed-on: https://gerrit.instructure.com/23303
Tested-by: Jenkins <jenkins@instructure.com>
Reviewed-by: Cameron Matheson <cameron@instructure.com>
QA-Review: Amber Taniuchi <amber@instructure.com>
Product-Review: Simon Williams <simon@instructure.com>
2013-08-14 07:15:15 +08:00
rubric_params = {
2013-11-27 04:43:48 +08:00
:title = > 'My Rubric' ,
:hide_score_total = > false ,
:criteria = > {
correctly maintain assignment/rubric <-> outcome links
There are a few old LearningOutcomeResults that have a nil artifact, and
many that have a Submission as an artifact. We want to get rid of these
because they come from bad data, and step 1 toward that goal is to stop
creating them and hide them in the UI.
The LORs with a nil artifact are very old and I believe came from a very
early incarnation of outcomes. The LORs with a Submission as an artifact
came from a combination of two code problems. The first was old code
that allowed an assignment that was aligned with an outcome but did not
have a rubric to create LORs directly based on it's submission. The
second was a bug that prevented the assignment <-> outcome link from
being destroyed when a rubric with an outcome was removed from an
assignment.
fixes CNVS-7495
fixes CNVS-7498
test plan:
- try different combinations of adding a rubric with an outcome to an
assignment.
- when you grade the assignment, the grade create a learning outcome result
(which can be seen on the outcome show page, or in the account outcome
report) if the rubric+outcome are currently attached to the assignment.
- so for example, add a rubric with an outcome, check, remove just the outcome
row, check, add a new outcome row, check, remove the whole rubric, check.
- be sure to check both the show page and the outcome report
TODO:
- datafix migration
Change-Id: I37700e3e5c08fc6cfb8fcf1cac42ea6693fcaba3
Reviewed-on: https://gerrit.instructure.com/23303
Tested-by: Jenkins <jenkins@instructure.com>
Reviewed-by: Cameron Matheson <cameron@instructure.com>
QA-Review: Amber Taniuchi <amber@instructure.com>
Product-Review: Simon Williams <simon@instructure.com>
2013-08-14 07:15:15 +08:00
" 0 " = > {
2013-11-27 04:43:48 +08:00
:points = > 3 ,
2014-03-08 16:07:36 +08:00
:mastery_points = > opts [ :mastery_points ] || 0 ,
2013-11-27 04:43:48 +08:00
:description = > " Outcome row " ,
:long_description = > @outcome . description ,
:ratings = > {
" 0 " = > {
:points = > 3 ,
:description = > " Rockin' " ,
} ,
" 1 " = > {
:points = > 0 ,
:description = > " Lame " ,
}
} ,
:learning_outcome_id = > @outcome . id
correctly maintain assignment/rubric <-> outcome links
There are a few old LearningOutcomeResults that have a nil artifact, and
many that have a Submission as an artifact. We want to get rid of these
because they come from bad data, and step 1 toward that goal is to stop
creating them and hide them in the UI.
The LORs with a nil artifact are very old and I believe came from a very
early incarnation of outcomes. The LORs with a Submission as an artifact
came from a combination of two code problems. The first was old code
that allowed an assignment that was aligned with an outcome but did not
have a rubric to create LORs directly based on it's submission. The
second was a bug that prevented the assignment <-> outcome link from
being destroyed when a rubric with an outcome was removed from an
assignment.
fixes CNVS-7495
fixes CNVS-7498
test plan:
- try different combinations of adding a rubric with an outcome to an
assignment.
- when you grade the assignment, the grade create a learning outcome result
(which can be seen on the outcome show page, or in the account outcome
report) if the rubric+outcome are currently attached to the assignment.
- so for example, add a rubric with an outcome, check, remove just the outcome
row, check, add a new outcome row, check, remove the whole rubric, check.
- be sure to check both the show page and the outcome report
TODO:
- datafix migration
Change-Id: I37700e3e5c08fc6cfb8fcf1cac42ea6693fcaba3
Reviewed-on: https://gerrit.instructure.com/23303
Tested-by: Jenkins <jenkins@instructure.com>
Reviewed-by: Cameron Matheson <cameron@instructure.com>
QA-Review: Amber Taniuchi <amber@instructure.com>
Product-Review: Simon Williams <simon@instructure.com>
2013-08-14 07:15:15 +08:00
} ,
" 1 " = > {
2013-11-27 04:43:48 +08:00
:points = > 5 ,
:description = > " no outcome row " ,
:long_description = > 'non outcome criterion' ,
:ratings = > {
" 0 " = > {
:points = > 5 ,
:description = > " Amazing " ,
} ,
" 1 " = > {
:points = > 3 ,
:description = > " not too bad " ,
} ,
" 2 " = > {
:points = > 0 ,
:description = > " no bueno " ,
}
}
correctly maintain assignment/rubric <-> outcome links
There are a few old LearningOutcomeResults that have a nil artifact, and
many that have a Submission as an artifact. We want to get rid of these
because they come from bad data, and step 1 toward that goal is to stop
creating them and hide them in the UI.
The LORs with a nil artifact are very old and I believe came from a very
early incarnation of outcomes. The LORs with a Submission as an artifact
came from a combination of two code problems. The first was old code
that allowed an assignment that was aligned with an outcome but did not
have a rubric to create LORs directly based on it's submission. The
second was a bug that prevented the assignment <-> outcome link from
being destroyed when a rubric with an outcome was removed from an
assignment.
fixes CNVS-7495
fixes CNVS-7498
test plan:
- try different combinations of adding a rubric with an outcome to an
assignment.
- when you grade the assignment, the grade create a learning outcome result
(which can be seen on the outcome show page, or in the account outcome
report) if the rubric+outcome are currently attached to the assignment.
- so for example, add a rubric with an outcome, check, remove just the outcome
row, check, add a new outcome row, check, remove the whole rubric, check.
- be sure to check both the show page and the outcome report
TODO:
- datafix migration
Change-Id: I37700e3e5c08fc6cfb8fcf1cac42ea6693fcaba3
Reviewed-on: https://gerrit.instructure.com/23303
Tested-by: Jenkins <jenkins@instructure.com>
Reviewed-by: Cameron Matheson <cameron@instructure.com>
QA-Review: Amber Taniuchi <amber@instructure.com>
Product-Review: Simon Williams <simon@instructure.com>
2013-08-14 07:15:15 +08:00
}
}
}
@rubric = @course . rubrics . build
@rubric . update_criteria ( rubric_params )
@rubric . reload
2011-08-04 23:48:19 +08:00
end
2012-12-15 03:49:40 +08:00
def grading_standard_for ( context , opts = { } )
@standard = context . grading_standards . create! (
2013-05-24 03:18:11 +08:00
:title = > opts [ :title ] || " My Grading Standard " ,
:standard_data = > {
" scheme_0 " = > { :name = > " A " , :value = > " 0.9 " } ,
" scheme_1 " = > { :name = > " B " , :value = > " 0.8 " } ,
" scheme_2 " = > { :name = > " C " , :value = > " 0.7 " }
} )
2012-02-25 05:44:45 +08:00
end
2011-02-01 09:57:29 +08:00
def eportfolio ( opts = { } )
2014-08-04 04:01:07 +08:00
user ( opts ) unless @user
2011-02-01 09:57:29 +08:00
@portfolio = @user . eportfolios . create!
end
2013-05-24 03:18:11 +08:00
2011-02-01 09:57:29 +08:00
def eportfolio_with_user ( opts = { } )
2014-08-04 04:01:07 +08:00
user ( opts )
2011-02-01 09:57:29 +08:00
eportfolio ( opts )
end
2013-05-24 03:18:11 +08:00
2011-09-23 03:27:37 +08:00
def conversation ( * users )
options = users . last . is_a? ( Hash ) ? users . pop : { }
2013-08-23 02:44:07 +08:00
@conversation = ( options . delete ( :sender ) || @me || users . shift ) . initiate_conversation ( users , options . delete ( :private ) )
2015-06-11 02:47:47 +08:00
# if the "body" hash is passed in, use that for the message body
if ! options [ :body ] . nil?
@message = @conversation . add_message ( options [ :body ] . to_s )
else
@message = @conversation . add_message ( 'test' )
end
2011-09-23 03:27:37 +08:00
@conversation . update_attributes ( options )
@conversation . reload
end
2011-10-08 05:41:19 +08:00
def media_object ( opts = { } )
mo = MediaObject . new
mo . media_id = opts [ :media_id ] || " 1234 "
mo . media_type = opts [ :media_type ] || " video "
2013-06-19 22:48:01 +08:00
mo . context = opts [ :context ] || @course
2011-10-08 05:41:19 +08:00
mo . user = opts [ :user ] || @user
mo . save!
2013-06-19 22:48:01 +08:00
mo
2011-10-08 05:41:19 +08:00
end
2011-02-01 09:57:29 +08:00
def assert_status ( status = 500 )
2014-10-17 03:02:40 +08:00
expect ( response . status . to_i ) . to eq status
2011-02-01 09:57:29 +08:00
end
def assert_unauthorized
refactor PseudonymSessionsController
fixes CNVS-20394
split it into appropriate concerns. main points are:
* /login never renders a login form - it redirects forward to the
default auth controller based on the first account
authorization config (or discovery url on the account)
* /login/canvas is the new home of the old login form. this form is
never rendered in-situ anymore - other places that used to render
it now redirect to /login (and then forward to here), reducing
their knowledge of SSO
* /login/ldap ends up at the same place (cause LDAP auth is handled
transparently)
* /login/cas and /login/saml redirect forward to the first SSO
configuration of the appropriate type. /login/:auth_type/:id can
be used to select a specific one
* if an SSO fails, it redirects back to /login with flash[:error]
set. this can forward to the discovery url appropriately, or
render an error page appropriately (the old no_auto=1, but now
it's not layered on top of the login partial that didn't show a
login form)
* ?canvas_login=1 is deprecated. just go directly to /login/canvas
* /saml_consume, /saml_logout are deprecated. they are processed
directly by /login/saml and /login/saml/logout
* /login/:id is deprecated - it forwards to /login/:auth_type/:id
as appropriate (presumably only saml, since that was the only
one that previously should have been using these links)
* OTP has been split into its own controller, and separated into
multiple actions instead of one all-in-one action
* /logout has been vastly simplified. the login controller should
set session[:login_aac], and on logout it will check with that
AAC for a url to redirect to after logout, instead of /login.
SSO logout is handled by each controller if they support it
test plan:
* regression test the following functionality -
* login with canvas auth
* login with LDAP auth
* login with SAML auth - and multiple SAMLs
* login with CAS auth
* MFA (configure, using, auto-setup)
* Canvas as OAuth Provider flow
* redirects to the login page when you're not
logged in
* failure of SAML/CAS (i.e. can't find user)
show a decent error page and allows retry
* "sticky" site admin auth (site admin is CAS/SAML,
going directly to another domain logs you in with
site admin)
Change-Id: I1bb9d81a101939f812cbd5020e20749e883fdc0f
Reviewed-on: https://gerrit.instructure.com/53220
QA-Review: August Thornton <august@instructure.com>
Tested-by: Jenkins
Reviewed-by: Ethan Vizitei <evizitei@instructure.com>
Product-Review: Cody Cutrer <cody@instructure.com>
2015-05-01 03:58:57 +08:00
# we allow either a raw unauthorized or a redirect to login
unless response . status == 401
expect ( response ) . to redirect_to ( login_url )
end
2011-02-01 09:57:29 +08:00
end
refactor PseudonymSessionsController
fixes CNVS-20394
split it into appropriate concerns. main points are:
* /login never renders a login form - it redirects forward to the
default auth controller based on the first account
authorization config (or discovery url on the account)
* /login/canvas is the new home of the old login form. this form is
never rendered in-situ anymore - other places that used to render
it now redirect to /login (and then forward to here), reducing
their knowledge of SSO
* /login/ldap ends up at the same place (cause LDAP auth is handled
transparently)
* /login/cas and /login/saml redirect forward to the first SSO
configuration of the appropriate type. /login/:auth_type/:id can
be used to select a specific one
* if an SSO fails, it redirects back to /login with flash[:error]
set. this can forward to the discovery url appropriately, or
render an error page appropriately (the old no_auto=1, but now
it's not layered on top of the login partial that didn't show a
login form)
* ?canvas_login=1 is deprecated. just go directly to /login/canvas
* /saml_consume, /saml_logout are deprecated. they are processed
directly by /login/saml and /login/saml/logout
* /login/:id is deprecated - it forwards to /login/:auth_type/:id
as appropriate (presumably only saml, since that was the only
one that previously should have been using these links)
* OTP has been split into its own controller, and separated into
multiple actions instead of one all-in-one action
* /logout has been vastly simplified. the login controller should
set session[:login_aac], and on logout it will check with that
AAC for a url to redirect to after logout, instead of /login.
SSO logout is handled by each controller if they support it
test plan:
* regression test the following functionality -
* login with canvas auth
* login with LDAP auth
* login with SAML auth - and multiple SAMLs
* login with CAS auth
* MFA (configure, using, auto-setup)
* Canvas as OAuth Provider flow
* redirects to the login page when you're not
logged in
* failure of SAML/CAS (i.e. can't find user)
show a decent error page and allows retry
* "sticky" site admin auth (site admin is CAS/SAML,
going directly to another domain logs you in with
site admin)
Change-Id: I1bb9d81a101939f812cbd5020e20749e883fdc0f
Reviewed-on: https://gerrit.instructure.com/53220
QA-Review: August Thornton <august@instructure.com>
Tested-by: Jenkins
Reviewed-by: Ethan Vizitei <evizitei@instructure.com>
Product-Review: Cody Cutrer <cody@instructure.com>
2015-05-01 03:58:57 +08:00
def assert_page_not_found
2014-07-24 01:14:22 +08:00
yield
assert_status ( 404 )
2014-02-08 02:41:40 +08:00
end
2011-02-01 09:57:29 +08:00
def assert_require_login
2014-10-17 03:02:40 +08:00
expect ( response ) . to be_redirect
expect ( flash [ :warning ] ) . to eq " You must be logged in to access this page "
2011-02-01 09:57:29 +08:00
end
2011-11-24 03:52:38 +08:00
2014-01-09 04:47:34 +08:00
def fixture_file_upload ( path , mime_type = nil , binary = false )
Rack :: Test :: UploadedFile . new ( File . join ( ActionController :: TestCase . fixture_path , path ) , mime_type , binary )
end
2011-02-01 09:57:29 +08:00
def default_uploaded_data
2014-01-09 04:47:34 +08:00
fixture_file_upload ( 'scribd_docs/doc.doc' , 'application/msword' , true )
2011-02-01 09:57:29 +08:00
end
2011-11-24 03:52:38 +08:00
2011-02-01 09:57:29 +08:00
def valid_gradebook_csv_content
File . read ( File . expand_path ( File . join ( File . dirname ( __FILE__ ) , %w( fixtures default_gradebook.csv ) ) ) )
end
def factory_with_protected_attributes ( ar_klass , attrs , do_save = true )
2011-05-21 06:29:34 +08:00
obj = ar_klass . respond_to? ( :new ) ? ar_klass . new : ar_klass . build
2013-05-24 03:18:11 +08:00
attrs . each { | k , v | obj . send ( " #{ k } = " , attrs [ k ] ) }
2011-02-01 09:57:29 +08:00
obj . save! if do_save
obj
end
def update_with_protected_attributes! ( ar_instance , attrs )
2013-05-24 03:18:11 +08:00
attrs . each { | k , v | ar_instance . send ( " #{ k } = " , attrs [ k ] ) }
2011-02-01 09:57:29 +08:00
ar_instance . save!
end
def update_with_protected_attributes ( ar_instance , attrs )
update_with_protected_attributes! ( ar_instance , attrs ) rescue false
end
2014-03-19 23:57:19 +08:00
def process_csv_data ( * lines )
opts = lines . extract_options!
opts . reverse_merge! ( allow_printing : false )
account = opts [ :account ] || @account || account_model
2011-09-22 01:36:45 +08:00
2011-06-14 04:39:15 +08:00
tmp = Tempfile . new ( " sis_rspec " )
path = " #{ tmp . path } .csv "
tmp . close!
2011-09-27 07:19:39 +08:00
File . open ( path , " w+ " ) { | f | f . puts lines . flatten . join " \n " }
2011-09-22 01:36:45 +08:00
opts [ :files ] = [ path ]
2011-11-24 03:52:38 +08:00
2014-03-19 23:57:19 +08:00
importer = SIS :: CSV :: Import . process ( account , opts )
2011-11-24 03:52:38 +08:00
2011-06-14 04:39:15 +08:00
File . unlink path
2011-11-24 03:52:38 +08:00
2011-06-14 04:39:15 +08:00
importer
end
2011-11-24 03:52:38 +08:00
2011-09-22 01:36:45 +08:00
def process_csv_data_cleanly ( * lines_or_opts )
importer = process_csv_data ( * lines_or_opts )
2014-10-17 03:02:40 +08:00
expect ( importer . errors ) . to eq [ ]
expect ( importer . warnings ) . to eq [ ]
2011-06-14 04:39:15 +08:00
end
2014-01-24 05:37:03 +08:00
def enable_cache ( new_cache = :memory_store )
2014-03-12 00:35:21 +08:00
new_cache || = :null_store
new_cache = ActiveSupport :: Cache . lookup_store ( new_cache )
previous_cache = Rails . cache
Rails . stubs ( :cache ) . returns ( new_cache )
ActionController :: Base . stubs ( :cache_store ) . returns ( new_cache )
ActionController :: Base . any_instance . stubs ( :cache_store ) . returns ( new_cache )
previous_perform_caching = ActionController :: Base . perform_caching
ActionController :: Base . stubs ( :perform_caching ) . returns ( true )
ActionController :: Base . any_instance . stubs ( :perform_caching ) . returns ( true )
if block_given?
begin
yield
ensure
Rails . stubs ( :cache ) . returns ( previous_cache )
ActionController :: Base . stubs ( :cache_store ) . returns ( previous_cache )
ActionController :: Base . any_instance . stubs ( :cache_store ) . returns ( previous_cache )
ActionController :: Base . stubs ( :perform_caching ) . returns ( previous_perform_caching )
ActionController :: Base . any_instance . stubs ( :perform_caching ) . returns ( previous_perform_caching )
end
end
2011-07-16 00:30:31 +08:00
end
2011-07-20 01:47:59 +08:00
# enforce forgery protection, so we can verify usage of the authenticity token
2013-03-12 04:17:20 +08:00
def enable_forgery_protection ( enable = true )
old_value = ActionController :: Base . allow_forgery_protection
2013-12-31 01:38:28 +08:00
ActionController :: Base . stubs ( :allow_forgery_protection ) . returns ( enable )
2014-02-26 01:15:17 +08:00
ActionController :: Base . any_instance . stubs ( :allow_forgery_protection ) . returns ( enable )
2012-03-21 06:08:20 +08:00
yield if block_given?
2011-07-20 01:47:59 +08:00
ensure
2014-02-26 01:15:17 +08:00
if block_given?
ActionController :: Base . stubs ( :allow_forgery_protection ) . returns ( old_value )
ActionController :: Base . any_instance . stubs ( :allow_forgery_protection ) . returns ( old_value )
end
2011-07-20 01:47:59 +08:00
end
2011-09-28 02:15:28 +08:00
def stub_kaltura
# trick kaltura into being activated
2014-03-18 01:38:50 +08:00
CanvasKaltura :: ClientV3 . stubs ( :config ) . returns ( {
2012-04-19 07:06:28 +08:00
'domain' = > 'kaltura.example.com' ,
'resource_domain' = > 'kaltura.example.com' ,
'partner_id' = > '100' ,
'subpartner_id' = > '10000' ,
'secret_key' = > 'fenwl1n23k4123lk4hl321jh4kl321j4kl32j14kl321' ,
'user_secret_key' = > '1234821hrj3k21hjk4j3kl21j4kl321j4kl3j21kl4j3k2l1' ,
'player_ui_conf' = > '1' ,
'kcw_ui_conf' = > '1' ,
'upload_ui_conf' = > '1'
} )
2011-09-28 02:15:28 +08:00
end
2011-08-12 00:51:57 +08:00
2011-10-21 03:52:36 +08:00
def attachment_obj_with_context ( obj , opts = { } )
@attachment = factory_with_protected_attributes ( Attachment , valid_attachment_attributes . merge ( opts ) )
@attachment . context = obj
@attachment
end
def attachment_with_context ( obj , opts = { } )
attachment_obj_with_context ( obj , opts )
@attachment . save!
@attachment
end
2011-11-24 04:49:27 +08:00
def json_parse ( json_string = response . body )
JSON . parse ( json_string . sub ( %r{ ^while \ (1 \ ); } , '' ) )
end
2012-02-22 04:35:39 +08:00
2013-03-07 03:44:57 +08:00
# inspired by http://blog.jayfields.com/2007/08/ruby-calling-methods-of-specific.html
module AttachmentStorageSwitcher
rails4: gemify attachment_fu
closes CNVS-14268
Since this is very clearly our own fork of the gem at this point, I've
removed a lot of unused code, rather than fixing it up to work as a gem.
This includes:
* all the other processors besides mini_magick
* red_artisan, it was only used by the core_image processor
* geometry and the Array monkey patch, it was only used by image science
* the db_file_backend
* the Tempfile monkey patch, I fixed the AttachmentFu code to properly
create tempfiles with the desired extension
* removed the Technoweenie outer namespace, to match normal gem practices
test plan:
Attachments should still work as before, including viewing, uploading,
downloading, and thumbnail generation.
Change-Id: I94ff63182af839ec54b64714defd6912b0d91f65
Reviewed-on: https://gerrit.instructure.com/41281
Tested-by: Jenkins <jenkins@instructure.com>
Reviewed-by: Cody Cutrer <cody@instructure.com>
QA-Review: August Thornton <august@instructure.com>
Product-Review: Brian Palmer <brianp@instructure.com>
2014-09-18 04:22:23 +08:00
BACKENDS = %w{ FileSystem S3 } . map { | backend | AttachmentFu :: Backends . const_get ( :" #{ backend } Backend " ) } . freeze
2013-03-07 03:44:57 +08:00
class As #:nodoc:
private * instance_methods . select { | m | m !~ / (^__|^ \ W|^binding$) / }
def initialize ( subject , ancestor )
@subject = subject
@ancestor = ancestor
end
def method_missing ( sym , * args , & blk )
2013-05-24 03:18:11 +08:00
@ancestor . instance_method ( sym ) . bind ( @subject ) . call ( * args , & blk )
2013-03-07 03:44:57 +08:00
end
end
def self . included ( base )
base . cattr_accessor :current_backend
base . current_backend = ( base . ancestors & BACKENDS ) . first
# make sure we have all the backends
BACKENDS . each do | backend |
base . send ( :include , backend ) unless base . ancestors . include? ( backend )
end
# remove the duplicate callbacks added by multiple backends
base . before_update . uniq!
BACKENDS . map ( & :instance_methods ) . flatten . uniq . each do | method |
2013-03-17 01:50:19 +08:00
# overridden by Attachment anyway; don't re-overwrite it
2014-08-20 06:31:40 +08:00
next if base . instance_method ( method ) . owner == base
2013-03-07 03:44:57 +08:00
if method . to_s [ - 1 .. - 1 ] == '='
base . class_eval <<-CODE
def #{method}(arg)
self . as ( self . class . current_backend ) . #{method} arg
end
CODE
else
base . class_eval <<-CODE
def #{method}(*args, &block)
self . as ( self . class . current_backend ) . #{method}(*args, &block)
end
CODE
end
end
end
def as ( ancestor )
@__as || = { }
unless r = @__as [ ancestor ]
r = ( @__as [ ancestor ] = As . new ( self , ancestor ) )
end
r
end
end
def s3_storage! ( opts = { :stubs = > true } )
2014-08-20 06:31:40 +08:00
[ Attachment , Thumbnail ] . each do | model |
model . send ( :include , AttachmentStorageSwitcher ) unless model . ancestors . include? ( AttachmentStorageSwitcher )
rails4: gemify attachment_fu
closes CNVS-14268
Since this is very clearly our own fork of the gem at this point, I've
removed a lot of unused code, rather than fixing it up to work as a gem.
This includes:
* all the other processors besides mini_magick
* red_artisan, it was only used by the core_image processor
* geometry and the Array monkey patch, it was only used by image science
* the db_file_backend
* the Tempfile monkey patch, I fixed the AttachmentFu code to properly
create tempfiles with the desired extension
* removed the Technoweenie outer namespace, to match normal gem practices
test plan:
Attachments should still work as before, including viewing, uploading,
downloading, and thumbnail generation.
Change-Id: I94ff63182af839ec54b64714defd6912b0d91f65
Reviewed-on: https://gerrit.instructure.com/41281
Tested-by: Jenkins <jenkins@instructure.com>
Reviewed-by: Cody Cutrer <cody@instructure.com>
QA-Review: August Thornton <august@instructure.com>
Product-Review: Brian Palmer <brianp@instructure.com>
2014-09-18 04:22:23 +08:00
model . stubs ( :current_backend ) . returns ( AttachmentFu :: Backends :: S3Backend )
2014-08-20 06:31:40 +08:00
model . stubs ( :s3_storage? ) . returns ( true )
model . stubs ( :local_storage? ) . returns ( false )
end
2013-03-07 03:44:57 +08:00
if opts [ :stubs ]
2013-03-08 03:28:42 +08:00
conn = mock ( 'AWS::S3::Client' )
2014-08-20 06:31:40 +08:00
AWS :: S3 :: S3Object . any_instance . stubs ( :read ) . returns ( " i am stub data from spec helper. nom nom nom " )
AWS :: S3 :: S3Object . any_instance . stubs ( :write ) . returns ( true )
AWS :: S3 :: S3Object . any_instance . stubs ( :create_temp_file ) . returns ( true )
2013-03-08 03:28:42 +08:00
AWS :: S3 :: S3Object . any_instance . stubs ( :client ) . returns ( conn )
AWS :: Core :: Configuration . any_instance . stubs ( :access_key_id ) . returns ( 'stub_id' )
AWS :: Core :: Configuration . any_instance . stubs ( :secret_access_key ) . returns ( 'stub_key' )
AWS :: S3 :: Bucket . any_instance . stubs ( :name ) . returns ( 'no-bucket' )
2013-03-07 03:44:57 +08:00
else
if Attachment . s3_config . blank? || Attachment . s3_config [ :access_key_id ] == 'access_key'
2014-10-17 03:03:02 +08:00
skip " Please put valid S3 credentials in config/amazon_s3.yml "
2013-03-07 03:44:57 +08:00
end
end
2014-10-17 03:02:40 +08:00
expect ( Attachment . s3_storage? ) . to be true
expect ( Attachment . local_storage? ) . to be false
2012-02-22 04:35:39 +08:00
end
def local_storage!
2014-08-20 06:31:40 +08:00
[ Attachment , Thumbnail ] . each do | model |
model . send ( :include , AttachmentStorageSwitcher ) unless model . ancestors . include? ( AttachmentStorageSwitcher )
rails4: gemify attachment_fu
closes CNVS-14268
Since this is very clearly our own fork of the gem at this point, I've
removed a lot of unused code, rather than fixing it up to work as a gem.
This includes:
* all the other processors besides mini_magick
* red_artisan, it was only used by the core_image processor
* geometry and the Array monkey patch, it was only used by image science
* the db_file_backend
* the Tempfile monkey patch, I fixed the AttachmentFu code to properly
create tempfiles with the desired extension
* removed the Technoweenie outer namespace, to match normal gem practices
test plan:
Attachments should still work as before, including viewing, uploading,
downloading, and thumbnail generation.
Change-Id: I94ff63182af839ec54b64714defd6912b0d91f65
Reviewed-on: https://gerrit.instructure.com/41281
Tested-by: Jenkins <jenkins@instructure.com>
Reviewed-by: Cody Cutrer <cody@instructure.com>
QA-Review: August Thornton <august@instructure.com>
Product-Review: Brian Palmer <brianp@instructure.com>
2014-09-18 04:22:23 +08:00
model . stubs ( :current_backend ) . returns ( AttachmentFu :: Backends :: FileSystemBackend )
2014-08-20 06:31:40 +08:00
model . stubs ( :s3_storage? ) . returns ( false )
model . stubs ( :local_storage? ) . returns ( true )
end
2013-03-07 03:44:57 +08:00
2014-10-17 03:02:40 +08:00
expect ( Attachment . local_storage? ) . to be true
expect ( Attachment . s3_storage? ) . to be false
2012-02-22 04:35:39 +08:00
end
2011-12-10 06:37:12 +08:00
refactor jobs admin functionality to not use AR queries
A set of class functions were added to Delayed::Backend::ActiveRecord
for all the querying a updating functionality that the jobs admin needs,
so that no direct ActiveRecord queries are needed. The /jobs UI is
refactored to use these new functions.
There are a few differences in behavior: The search isn't a combined
wildcard search anymore. Instead, new "flavors" were added to the
drop-down for strand, tag, and ID. The search box searches only the
selected attribute, and it's exact match now.
Specs are being updated to use these new functions as well. Eventually,
no direct AR queries will be done against Jobs anywhere, so that non-AR
jobs backends are possible.
Also as part of this, all jobs require a queue now. Passing nil for the
queue will use the default of Delayed::Worker.queue.
test plan: Load /jobs, and verify that it works as before except where
there are differences as described above.
* Selecting flavors of jobs lists only those jobs.
* Searching by ID, strand or tag works.
* The hold/unhold/delete actions work in the various combinations of
filtering/searching.
* Linking to an individual job still works (though the query string
has changed so old links don't work)
* Running jobs and list of popular tags still works as expected.
Change-Id: Iffd5b8c7b3d6e4b128792a9dee7b97c6dfb251dc
Reviewed-on: https://gerrit.instructure.com/12632
Tested-by: Jenkins <jenkins@instructure.com>
Reviewed-by: Bracken Mosbacker <bracken@instructure.com>
Reviewed-on: https://gerrit.instructure.com/13089
Reviewed-by: Jacob Fugal <jacob@instructure.com>
Tested-by: Jacob Fugal <jacob@instructure.com>
2012-08-01 04:22:52 +08:00
def run_job ( job )
2011-12-10 06:37:12 +08:00
Delayed :: Worker . new . perform ( job )
end
2012-03-21 06:08:20 +08:00
2012-06-07 04:16:55 +08:00
def run_jobs
while job = Delayed :: Job . get_and_lock_next_available (
2013-05-24 03:18:11 +08:00
'spec run_jobs' ,
2014-10-01 02:41:19 +08:00
Delayed :: Settings . queue ,
2013-05-24 03:18:11 +08:00
0 ,
Delayed :: MAX_PRIORITY )
2012-06-07 04:16:55 +08:00
run_job ( job )
end
end
refactor jobs admin functionality to not use AR queries
A set of class functions were added to Delayed::Backend::ActiveRecord
for all the querying a updating functionality that the jobs admin needs,
so that no direct ActiveRecord queries are needed. The /jobs UI is
refactored to use these new functions.
There are a few differences in behavior: The search isn't a combined
wildcard search anymore. Instead, new "flavors" were added to the
drop-down for strand, tag, and ID. The search box searches only the
selected attribute, and it's exact match now.
Specs are being updated to use these new functions as well. Eventually,
no direct AR queries will be done against Jobs anywhere, so that non-AR
jobs backends are possible.
Also as part of this, all jobs require a queue now. Passing nil for the
queue will use the default of Delayed::Worker.queue.
test plan: Load /jobs, and verify that it works as before except where
there are differences as described above.
* Selecting flavors of jobs lists only those jobs.
* Searching by ID, strand or tag works.
* The hold/unhold/delete actions work in the various combinations of
filtering/searching.
* Linking to an individual job still works (though the query string
has changed so old links don't work)
* Running jobs and list of popular tags still works as expected.
Change-Id: Iffd5b8c7b3d6e4b128792a9dee7b97c6dfb251dc
Reviewed-on: https://gerrit.instructure.com/12632
Tested-by: Jenkins <jenkins@instructure.com>
Reviewed-by: Bracken Mosbacker <bracken@instructure.com>
Reviewed-on: https://gerrit.instructure.com/13089
Reviewed-by: Jacob Fugal <jacob@instructure.com>
Tested-by: Jacob Fugal <jacob@instructure.com>
2012-08-01 04:22:52 +08:00
def track_jobs
@jobs_tracking = Delayed :: JobTracking . track { yield }
end
def created_jobs
@jobs_tracking . created
end
def expects_job_with_tag ( tag , count = 1 )
track_jobs do
yield
end
2014-10-17 03:02:40 +08:00
expect ( created_jobs . count { | j | j . tag == tag } ) . to eq count
refactor jobs admin functionality to not use AR queries
A set of class functions were added to Delayed::Backend::ActiveRecord
for all the querying a updating functionality that the jobs admin needs,
so that no direct ActiveRecord queries are needed. The /jobs UI is
refactored to use these new functions.
There are a few differences in behavior: The search isn't a combined
wildcard search anymore. Instead, new "flavors" were added to the
drop-down for strand, tag, and ID. The search box searches only the
selected attribute, and it's exact match now.
Specs are being updated to use these new functions as well. Eventually,
no direct AR queries will be done against Jobs anywhere, so that non-AR
jobs backends are possible.
Also as part of this, all jobs require a queue now. Passing nil for the
queue will use the default of Delayed::Worker.queue.
test plan: Load /jobs, and verify that it works as before except where
there are differences as described above.
* Selecting flavors of jobs lists only those jobs.
* Searching by ID, strand or tag works.
* The hold/unhold/delete actions work in the various combinations of
filtering/searching.
* Linking to an individual job still works (though the query string
has changed so old links don't work)
* Running jobs and list of popular tags still works as expected.
Change-Id: Iffd5b8c7b3d6e4b128792a9dee7b97c6dfb251dc
Reviewed-on: https://gerrit.instructure.com/12632
Tested-by: Jenkins <jenkins@instructure.com>
Reviewed-by: Bracken Mosbacker <bracken@instructure.com>
Reviewed-on: https://gerrit.instructure.com/13089
Reviewed-by: Jacob Fugal <jacob@instructure.com>
Tested-by: Jacob Fugal <jacob@instructure.com>
2012-08-01 04:22:52 +08:00
end
2012-03-21 06:08:20 +08:00
# send a multipart post request in an integration spec post_params is
# an array of [k,v] params so that the order of the params can be
# defined
def send_multipart ( url , post_params = { } , http_headers = { } , method = :post )
2014-02-13 04:05:32 +08:00
mp = Multipart :: Post . new
2012-03-21 06:08:20 +08:00
query , headers = mp . prepare_query ( post_params )
2014-03-26 00:00:24 +08:00
# A bug in the testing adapter in Rails 3-2-stable doesn't corretly handle
# translating this header to the Rack/CGI compatible version:
# (https://github.com/rails/rails/blob/3-2-stable/actionpack/lib/action_dispatch/testing/integration.rb#L289)
#
# This issue is fixed in Rails 4-0 stable, by using a newer version of
# ActionDispatch Http::Headers which correctly handles the merge
headers = headers . dup . tap { | h | h [ 'CONTENT_TYPE' ] || = h . delete ( 'Content-type' ) }
2012-03-21 06:08:20 +08:00
send ( method , url , query , headers . merge ( http_headers ) )
end
2012-03-30 07:52:51 +08:00
2012-11-01 23:03:48 +08:00
def force_string_encoding ( str , encoding = " UTF-8 " )
if str . respond_to? ( :force_encoding )
str . force_encoding ( encoding )
end
str
end
2013-02-05 05:58:38 +08:00
# from minitest, MIT licensed
def capture_io
orig_stdout , orig_stderr = $stdout , $stderr
$stdout , $stderr = StringIO . new , StringIO . new
yield
return $stdout . string , $stderr . string
ensure
$stdout , $stderr = orig_stdout , orig_stderr
end
2012-05-18 10:51:27 +08:00
def verify_post_matches ( post_lines , expected_post_lines )
# first lines should match
2014-10-17 03:02:40 +08:00
expect ( post_lines [ 0 ] ) . to eq expected_post_lines [ 0 ]
2012-05-18 10:51:27 +08:00
# now extract the headers
post_headers = post_lines [ 1 .. post_lines . index ( " " ) ]
expected_post_headers = expected_post_lines [ 1 .. expected_post_lines . index ( " " ) ]
2013-04-19 23:54:35 +08:00
expected_post_headers << " User-Agent: Ruby "
2014-10-17 03:02:40 +08:00
expect ( post_headers . sort ) . to eq expected_post_headers . sort
2012-05-18 10:51:27 +08:00
# now check payload
2014-10-17 03:02:40 +08:00
expect ( post_lines [ post_lines . index ( " " ) , - 1 ] ) . to eq
2013-05-24 03:18:11 +08:00
expected_post_lines [ expected_post_lines . index ( " " ) , - 1 ]
2012-05-18 10:51:27 +08:00
end
2012-08-14 05:13:20 +08:00
2013-01-01 06:25:44 +08:00
def compare_json ( actual , expected )
if actual . is_a? ( Hash )
2013-05-24 03:18:11 +08:00
actual . each do | k , v |
2013-01-01 06:25:44 +08:00
expected_v = expected [ k ]
compare_json ( v , expected_v )
end
elsif actual . is_a? ( Array )
2013-05-24 03:18:11 +08:00
actual . zip ( expected ) . each do | a , e |
compare_json ( a , e )
2013-01-01 06:25:44 +08:00
end
else
2014-02-26 22:50:20 +08:00
if actual . is_a? ( Fixnum ) || actual . is_a? ( Float )
2014-10-17 03:02:40 +08:00
expect ( actual ) . to eq expected
2014-02-26 22:50:20 +08:00
else
2014-10-17 03:02:40 +08:00
expect ( actual . to_json ) . to eq expected . to_json
2014-02-26 22:50:20 +08:00
end
2013-01-01 06:25:44 +08:00
end
end
2012-08-14 05:13:20 +08:00
class FakeHttpResponse
def initialize ( code , body = nil , headers = { } )
@code = code
@body = body
@headers = headers
end
def read_body ( io )
io << @body
end
def code
@code . to_s
end
def [] ( arg )
@headers [ arg ]
end
def content_type
self [ 'content-type' ]
end
end
2013-03-14 23:27:50 +08:00
def intify_timestamps ( object )
case object
2013-05-24 03:18:11 +08:00
when Time
object . to_i
when Hash
object . inject ( { } ) { | memo , ( k , v ) | memo [ intify_timestamps ( k ) ] = intify_timestamps ( v ) ; memo }
when Array
object . map { | v | intify_timestamps ( v ) }
else
object
2013-03-14 23:27:50 +08:00
end
end
2013-03-16 22:50:49 +08:00
2014-07-04 12:38:20 +08:00
# frd class, not a mock, so we can once-ler WebConferences (need to Marshal.dump)
class WebConferencePluginMock
attr_reader :id , :settings
def initialize ( id , settings )
@id = id
@settings = settings
end
2015-04-28 01:47:25 +08:00
2014-07-04 12:38:20 +08:00
def valid_settings? ; true ; end
2015-04-28 01:47:25 +08:00
2014-07-04 12:38:20 +08:00
def enabled? ; true ; end
2015-04-28 01:47:25 +08:00
2014-07-04 12:38:20 +08:00
def base ; end
end
2013-03-16 22:50:49 +08:00
def web_conference_plugin_mock ( id , settings )
2014-07-04 12:38:20 +08:00
WebConferencePluginMock . new ( id , settings )
2013-03-16 22:50:49 +08:00
end
2013-05-07 03:16:06 +08:00
def dummy_io
2014-01-09 04:47:34 +08:00
fixture_file_upload ( 'scribd_docs/doc.doc' , 'application/msword' , true )
2013-05-07 03:16:06 +08:00
end
2013-05-24 03:18:11 +08:00
def create_attachment_for_file_upload_submission! ( submission , opts = { } )
submission . attachments . create! opts . merge (
:filename = > " doc.doc " ,
:display_name = > " doc.doc " , :user = > @user ,
:uploaded_data = > dummy_io )
2013-05-07 03:16:06 +08:00
end
def course_quiz ( active = false )
@quiz = @course . quizzes . create
@quiz . workflow_state = " available " if active
@quiz . save!
@quiz
end
2013-08-29 06:20:24 +08:00
def n_students_in_course ( n , opts = { } )
opts . reverse_merge active_all : true
n . times . map { student_in_course ( opts ) ; @student }
end
2013-10-29 05:31:58 +08:00
def consider_all_requests_local ( value )
2014-07-24 01:14:22 +08:00
Rails . application . config . consider_all_requests_local = value
2013-10-29 05:31:58 +08:00
end
2013-11-05 02:56:25 +08:00
def page_view_for ( opts = { } )
@account = opts [ :account ] || Account . default
@context = opts [ :context ] || course ( opts )
@request_id = opts [ :request_id ] || RequestContextGenerator . request_id
unless @request_id
2015-04-09 01:21:08 +08:00
@request_id = SecureRandom . uuid
2013-11-27 04:43:48 +08:00
RequestContextGenerator . stubs ( :request_id = > @request_id )
2013-11-05 02:56:25 +08:00
end
Setting . set ( 'enable_page_views' , 'db' )
@page_view = PageView . new { | p |
2014-02-05 02:43:07 +08:00
p . assign_attributes ( {
2014-02-27 14:04:17 +08:00
:id = > @request_id ,
:url = > " http://test.one/ " ,
:session_id = > " phony " ,
:context = > @context ,
:controller = > opts [ :controller ] || 'courses' ,
:action = > opts [ :action ] || 'show' ,
:user_request = > true ,
:render_time = > 0 . 01 ,
:user_agent = > 'None' ,
:account_id = > @account . id ,
:request_id = > request_id ,
:interaction_seconds = > 5 ,
:user = > @user ,
:remote_ip = > '192.168.0.42'
} , :without_protection = > true )
2013-11-05 02:56:25 +08:00
}
@page_view . save!
@page_view
end
2014-05-16 00:23:58 +08:00
# a fast way to create a record, especially if you don't need the actual
# ruby object. since it just does a straight up insert, you need to
# provide any non-null attributes or things that would normally be
# inferred/defaulted prior to saving
def create_record ( klass , attributes , return_type = :id )
create_records ( klass , [ attributes ] , return_type ) [ 0 ]
end
# a little wrapper around bulk_insert that gives you back records or ids
# in order
# NOTE: if you decide you want to go add something like this to canvas
# proper, make sure you have it handle concurrent inserts (this does
# not, because READ COMMITTED is the default transaction isolation
# level)
def create_records ( klass , records , return_type = :id )
return [ ] if records . empty?
klass . transaction do
klass . connection . bulk_insert klass . table_name , records
scope = klass . order ( " id DESC " ) . limit ( records . size )
return_type == :record ?
scope . all . reverse :
scope . pluck ( :id ) . reverse
end
end
2014-07-13 15:07:19 +08:00
# create a bunch of courses at once, optionally enrolling a user in them
# records can either be the number of records to create, or an array of
# hashes of attributes you want to insert
def create_courses ( records , options = { } )
account = options [ :account ] || Account . default
records = records . times . map { { } } if records . is_a? ( Fixnum )
records = records . map { | record | course_valid_attributes . merge ( account_id : account . id , root_account_id : account . id , workflow_state : 'available' , enrollment_term_id : account . default_enrollment_term . id ) . merge ( record ) }
course_data = create_records ( Course , records , options [ :return_type ] )
course_ids = options [ :return_type ] == :record ?
course_data . map ( & :id ) :
course_data
if options [ :account_associations ]
create_records ( CourseAccountAssociation , course_ids . map { | id | { account_id : account . id , course_id : id , depth : 0 } } )
end
if user = options [ :enroll_user ]
section_ids = create_records ( CourseSection , course_ids . map { | id | { course_id : id , root_account_id : account . id , name : " Default Section " , default_section : true } } )
type = options [ :enrollment_type ] || " TeacherEnrollment "
2014-12-12 21:56:39 +08:00
create_records ( Enrollment , course_ids . each_with_index . map { | id , i | { course_id : id , user_id : user . id , type : type , course_section_id : section_ids [ i ] , root_account_id : account . id , workflow_state : 'active' , :role_id = > Role . get_built_in_role ( type ) . id } } )
2014-07-13 15:07:19 +08:00
end
course_data
end
def create_users ( records , options = { } )
records = records . times . map { { } } if records . is_a? ( Fixnum )
records = records . map { | record | valid_user_attributes . merge ( workflow_state : " registered " ) . merge ( record ) }
create_records ( User , records , options [ :return_type ] )
end
# create a bunch of users at once, and enroll them all in the same course
def create_users_in_course ( course , records , options = { } )
user_data = create_users ( records , options )
create_enrollments ( course , user_data , options )
user_data
end
def create_enrollments ( course , users , options = { } )
user_ids = users . first . is_a? ( User ) ?
users . map ( & :id ) :
users
2014-07-30 15:18:46 +08:00
if options [ :account_associations ]
create_records ( UserAccountAssociation , user_ids . map { | id | { account_id : course . account_id , user_id : id , depth : 0 } } )
end
2014-07-13 15:07:19 +08:00
section_id = options [ :section_id ] || course . default_section . id
type = options [ :enrollment_type ] || " StudentEnrollment "
2014-12-12 21:56:39 +08:00
create_records ( Enrollment , user_ids . map { | id | { course_id : course . id , user_id : id , type : type , course_section_id : section_id , root_account_id : course . account . id , workflow_state : 'active' , :role_id = > Role . get_built_in_role ( type ) . id } } , options [ :return_type ] )
2014-07-13 15:07:19 +08:00
end
def create_assignments ( course_ids , count_per_course = 1 , fields = { } )
course_ids = Array ( course_ids )
course_ids *= count_per_course
2014-11-12 08:15:00 +08:00
create_records ( Assignment , course_ids . each_with_index . map { | id , i | { context_id : id , context_type : 'Course' , context_code : " course_ #{ id } " , title : " #{ id } : #{ i } " , grading_type : " points " , submission_types : " none " , workflow_state : 'published' } . merge ( fields ) } )
2014-07-13 15:07:19 +08:00
end
2011-02-01 09:57:29 +08:00
end
2012-06-22 06:34:27 +08:00
2014-08-01 14:35:17 +08:00
class I18nema :: Backend
def stub ( translations )
@stubs = translations . with_indifferent_access
singleton_class . instance_eval do
alias_method :lookup , :lookup_with_stubs
alias_method :available_locales , :available_locales_with_stubs
end
yield
ensure
singleton_class . instance_eval do
alias_method :lookup , :lookup_without_stubs
alias_method :available_locales , :available_locales_without_stubs
end
@stubs = nil
end
def lookup_with_stubs ( locale , key , scope = [ ] , options = { } )
init_translations unless initialized?
keys = normalize_keys ( locale , key , scope , options [ :separator ] )
keys . inject ( @stubs ) { | h , k | h [ k ] if h . respond_to? ( :key ) } || direct_lookup ( * keys )
end
alias_method :lookup_without_stubs , :lookup
def available_locales_with_stubs
available_locales_without_stubs | @stubs . keys . map ( & :to_sym )
end
alias_method :available_locales_without_stubs , :available_locales
end
2014-03-28 07:29:27 +08:00
class String
def red ; colorize ( self , " \e [1m \e [31m " ) ; end
2015-04-28 01:47:25 +08:00
2014-03-28 07:29:27 +08:00
def green ; colorize ( self , " \e [1m \e [32m " ) ; end
2015-04-28 01:47:25 +08:00
2014-03-28 07:29:27 +08:00
def dark_green ; colorize ( self , " \e [32m " ) ; end
2015-04-28 01:47:25 +08:00
2014-03-28 07:29:27 +08:00
def yellow ; colorize ( self , " \e [1m \e [33m " ) ; end
2015-04-28 01:47:25 +08:00
2014-03-28 07:29:27 +08:00
def blue ; colorize ( self , " \e [1m \e [34m " ) ; end
2015-04-28 01:47:25 +08:00
2014-03-28 07:29:27 +08:00
def dark_blue ; colorize ( self , " \e [34m " ) ; end
2015-04-28 01:47:25 +08:00
2014-03-28 07:29:27 +08:00
def pur ; colorize ( self , " \e [1m \e [35m " ) ; end
2015-04-28 01:47:25 +08:00
2014-03-28 07:29:27 +08:00
def colorize ( text , color_code ) " #{ color_code } #{ text } \e [0m " end
end
2014-07-30 05:33:55 +08:00
Dir [ Rails . root + '{gems,vendor}/plugins/*/spec_canvas/spec_helper.rb' ] . each do | f |
2012-06-22 06:34:27 +08:00
require f
2013-05-21 03:39:36 +08:00
end