stellar 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.project +18 -0
- data/.rspec +3 -0
- data/Gemfile +21 -0
- data/Gemfile.lock +75 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +18 -0
- data/Rakefile +76 -0
- data/VERSION +1 -0
- data/lib/stellar.rb +18 -0
- data/lib/stellar/auth.rb +130 -0
- data/lib/stellar/client.rb +66 -0
- data/lib/stellar/courses.rb +112 -0
- data/lib/stellar/homework.rb +242 -0
- data/lib/stellar/mitca.crt +21 -0
- data/spec/fixtures/.gitkeep +0 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/stellar/auth_spec.rb +32 -0
- data/spec/stellar/client_spec.rb +58 -0
- data/spec/stellar/courses_spec.rb +41 -0
- data/spec/stellar/homework_spec.rb +32 -0
- data/spec/stellar/homework_submissions_spec.rb +124 -0
- data/spec/support/test-credentials.rb +24 -0
- metadata +205 -0
@@ -0,0 +1,112 @@
|
|
1
|
+
# :nodoc: namespace
|
2
|
+
module Stellar
|
3
|
+
|
4
|
+
# Course search functionality.
|
5
|
+
class Courses
|
6
|
+
def initialize(client)
|
7
|
+
@client = client
|
8
|
+
end
|
9
|
+
|
10
|
+
# My classes.
|
11
|
+
# @return [Array] array with one Hash per class; Hashes have :number and
|
12
|
+
# :url keys
|
13
|
+
def mine
|
14
|
+
page = @client.get_nokogiri '/atstellar'
|
15
|
+
class_links = page.css('a[href*="/S/course/"]').
|
16
|
+
map { |link| Stellar::Course.from_link link, @client }.reject(&:nil?)
|
17
|
+
end
|
18
|
+
end # class Stellar::Courses
|
19
|
+
|
20
|
+
# Stellar client scoped to a course.
|
21
|
+
class Course
|
22
|
+
# Official MIT course ID, e.g. "6.006".
|
23
|
+
attr_reader :number
|
24
|
+
|
25
|
+
# URL to the course's main page on Stellar.
|
26
|
+
#
|
27
|
+
# Example: "https://stellar.mit.edu/S/course/6/fa11/6.006/"
|
28
|
+
attr_reader :url
|
29
|
+
|
30
|
+
# Maps the text in navigation links to URI objects.
|
31
|
+
#
|
32
|
+
# Example: navigation['Homework'] => <# URI: .../ >
|
33
|
+
attr_reader :navigation
|
34
|
+
|
35
|
+
# True if the client has administrative rights for this course.
|
36
|
+
attr_reader :is_admin
|
37
|
+
|
38
|
+
# The generic Stellar client used to query the server.
|
39
|
+
attr_reader :client
|
40
|
+
|
41
|
+
# Creates a scoped Stellar client from a link to the course's page.
|
42
|
+
#
|
43
|
+
# @param [Nokogiri::XML::Element] nokogiri_link a link pointing to the
|
44
|
+
# course's main page
|
45
|
+
# @param [Stellar::Client] client generic Stellar client
|
46
|
+
# @return [Stellar::Course] client scoped to the course, or nil if the link
|
47
|
+
# is not valid
|
48
|
+
def self.from_link(nokogiri_link, client)
|
49
|
+
number = nokogiri_link.css('span.courseNo').inner_text
|
50
|
+
return nil if number.empty?
|
51
|
+
url = nokogiri_link['href']
|
52
|
+
return nil unless url.index(number)
|
53
|
+
return nil unless /\/S\/course\// =~ url
|
54
|
+
|
55
|
+
return self.new(client, url, number)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Creates a scoped Stellar client from a link to the course's page.
|
59
|
+
#
|
60
|
+
# @param [String] number the official MIT course ID, e.g. "6.006"
|
61
|
+
# @param [Fixnum] year the year the course was taught e.g. 2011
|
62
|
+
# @param [Symbol] semester :fall, :iap, :spring, :summer
|
63
|
+
# @return [Stellar::Course] client scoped to the course
|
64
|
+
def self.for(number, year, semester, client)
|
65
|
+
semester_string = case semester
|
66
|
+
when :fall
|
67
|
+
'fa'
|
68
|
+
when :spring
|
69
|
+
'sp'
|
70
|
+
when :summer
|
71
|
+
'su'
|
72
|
+
when :iap
|
73
|
+
'ia'
|
74
|
+
end
|
75
|
+
term = "#{semester_string}#{year.to_s[-2..-1]}"
|
76
|
+
major = number.split('.', 2).first
|
77
|
+
url = "/S/course/#{major}/#{term}/#{number}/index.html"
|
78
|
+
|
79
|
+
return self.new(client, url, number)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Creates a scoped Stellar client from detailed specifications.
|
83
|
+
#
|
84
|
+
# @param [Stellar::Client] client generic Stellar client
|
85
|
+
# @param [String] course_url HTTP URI to the course's main Stellar page
|
86
|
+
# @param [String] course_number official course ID, e.g. "6.006"
|
87
|
+
# @raise ArgumentError if the course URL does not point to a course page
|
88
|
+
def initialize(client, course_url, course_number)
|
89
|
+
@client = client
|
90
|
+
@url = course_url
|
91
|
+
@number = course_number
|
92
|
+
|
93
|
+
course_page = @client.get_nokogiri course_url
|
94
|
+
|
95
|
+
@is_admin = course_page.css('p#toolset').length > 0
|
96
|
+
|
97
|
+
navbar_elems = course_page.css('#mainnav')
|
98
|
+
unless navbar_elems.length == 1
|
99
|
+
raise ArgumentError, "#{course_url} is not a course page"
|
100
|
+
end
|
101
|
+
@navigation = Hash[navbar_elems.first.css('a').map do |link|
|
102
|
+
[link.inner_text, URI.join(course_page.url, link['href'])]
|
103
|
+
end]
|
104
|
+
end
|
105
|
+
|
106
|
+
# Client scoped to the course's Homework module.
|
107
|
+
def homework
|
108
|
+
@homework ||= Stellar::HomeworkList.new self
|
109
|
+
end
|
110
|
+
end # class Stellar::Course
|
111
|
+
|
112
|
+
end # namespace Stellar
|
@@ -0,0 +1,242 @@
|
|
1
|
+
# :nodoc: namespace
|
2
|
+
module Stellar
|
3
|
+
|
4
|
+
# Homework listing functionality.
|
5
|
+
class HomeworkList
|
6
|
+
# Creates a Stellar client scoped to a course's Homework module.
|
7
|
+
#
|
8
|
+
# @param [Stellar::Course] the course whose homework is desired
|
9
|
+
def initialize(course)
|
10
|
+
@course = course
|
11
|
+
@client = course.client
|
12
|
+
@url = course.navigation['Homework']
|
13
|
+
|
14
|
+
page = @client.get_nokogiri @url
|
15
|
+
@assignments = page.css('#content a[href*="assignment"]').map { |link|
|
16
|
+
name = link.inner_text
|
17
|
+
url = URI.join page.url, link['href']
|
18
|
+
begin
|
19
|
+
Stellar::Homework.new url, name, course
|
20
|
+
rescue ArgumentError
|
21
|
+
nil
|
22
|
+
end
|
23
|
+
}.reject(&:nil?)
|
24
|
+
end
|
25
|
+
|
26
|
+
# All assignments in this course's homework module.
|
27
|
+
# @return [Array<Stellar::Homework>] list of assignments posted by this course
|
28
|
+
def all
|
29
|
+
@assignments
|
30
|
+
end
|
31
|
+
|
32
|
+
# All assignments in this course's homework module.
|
33
|
+
# @return [Array<Stellar::Homework>] list of assignments posted by this course
|
34
|
+
def named(name)
|
35
|
+
@assignments.find { |a| a.name == name }
|
36
|
+
end
|
37
|
+
end # class Stellar::HomeworkList
|
38
|
+
|
39
|
+
# One assignment in the homework tab.
|
40
|
+
class Homework
|
41
|
+
# Assignment name.
|
42
|
+
attr_reader :name
|
43
|
+
|
44
|
+
# The course that this assignment is for.
|
45
|
+
attr_reader :course
|
46
|
+
|
47
|
+
# Generic Stellar client used to make requests.
|
48
|
+
attr_reader :client
|
49
|
+
|
50
|
+
# Creates a Stellar client scoped to an assignment.
|
51
|
+
#
|
52
|
+
# @param [URI, String] page_url URL to the assignment's main Stellar page
|
53
|
+
# @param [String] assignment name, e.g. "name"
|
54
|
+
# @param [Course] the course that issued the assignment
|
55
|
+
def initialize(page_url, name, course)
|
56
|
+
@name = name
|
57
|
+
@url = page_url
|
58
|
+
@course = course
|
59
|
+
@client = course.client
|
60
|
+
|
61
|
+
page = @client.get_nokogiri @url
|
62
|
+
unless page.css('#content p b').any? { |dom| dom.inner_text.strip == name }
|
63
|
+
raise ArgumentError, 'Invalid homework page URL'
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# List of submissions associated with this problem set.
|
68
|
+
def submissions
|
69
|
+
page = @client.get_nokogiri @url
|
70
|
+
@submissions ||= page.css('.gradeTable tbody tr').map { |tr|
|
71
|
+
begin
|
72
|
+
Stellar::Homework::Submission.new tr, self
|
73
|
+
rescue ArgumentError
|
74
|
+
nil
|
75
|
+
end
|
76
|
+
}.reject(&:nil?)
|
77
|
+
end
|
78
|
+
|
79
|
+
# A student's submission for an assignment.
|
80
|
+
class Submission
|
81
|
+
# URL to the last file that the student submitted.
|
82
|
+
attr_reader :file_url
|
83
|
+
|
84
|
+
# Name of the student who authored this submission.
|
85
|
+
attr_reader :name
|
86
|
+
|
87
|
+
# Email of the student who authorted this submission.
|
88
|
+
attr_reader :email
|
89
|
+
|
90
|
+
# Comments posted on this submission.
|
91
|
+
attr_reader :comments
|
92
|
+
|
93
|
+
# Homework that the submission belongs to.
|
94
|
+
attr_reader :homework
|
95
|
+
|
96
|
+
# Generic Stellar client used to make requests.
|
97
|
+
attr_reader :client
|
98
|
+
|
99
|
+
# Creates a submission from a <tr> element in the Stellar homework page.
|
100
|
+
#
|
101
|
+
# @param [Nokogiri::XML::Element]
|
102
|
+
def initialize(tr, homework)
|
103
|
+
link = tr.css('a').find { |link| /submission\s+details/ =~ link.inner_text }
|
104
|
+
raise ArgumentError, 'Invalid submission-listing <tr>' unless link
|
105
|
+
|
106
|
+
@url = URI.join tr.document.url, link['href']
|
107
|
+
@homework = homework
|
108
|
+
@client = homework.client
|
109
|
+
|
110
|
+
page = @client.get_nokogiri @url
|
111
|
+
|
112
|
+
unless author_link = page.css('#content h4 a[href^="mailto:"]').first
|
113
|
+
raise ArgumentError, 'Invalud submission-listing <tr>'
|
114
|
+
end
|
115
|
+
@name = author_link.inner_text
|
116
|
+
@email = author_link['href'].sub /^mailto:/, ''
|
117
|
+
@file_url = page.css('#rosterBox a[href*="studentWork"]').map { |link|
|
118
|
+
next nil unless link.inner_text == homework.name
|
119
|
+
URI.join @url.to_s, link['href']
|
120
|
+
}.reject(&:nil?).first
|
121
|
+
|
122
|
+
@add_comment_url = URI.join @url.to_s,
|
123
|
+
page.css('#comments a[href*="add"]').first['href']
|
124
|
+
reload_comments! page
|
125
|
+
end
|
126
|
+
|
127
|
+
# The contents of the file attached to this Stellar submission.
|
128
|
+
#
|
129
|
+
# @return [String] raw file data
|
130
|
+
def file_data
|
131
|
+
@client.get_file @file_url
|
132
|
+
end
|
133
|
+
|
134
|
+
# Adds a comment to the student's submission.
|
135
|
+
#
|
136
|
+
# @param [String] text the comment text
|
137
|
+
# @param [String] file_data the content of the file attached to the comment;
|
138
|
+
# by default, no file is attached
|
139
|
+
# @param [String] file_mime_type if a file is attached, indicates its type;
|
140
|
+
# examples: 'text/plain', 'application/pdf'
|
141
|
+
# @param [String] file_name name of the file attached to the comment; by
|
142
|
+
# default, 'attachment.txt'
|
143
|
+
# @return [Stellar::Homework::Submission] self
|
144
|
+
def add_comment(text, file_data = nil, file_mime_type = 'text/plain',
|
145
|
+
file_name = 'attachment.txt')
|
146
|
+
add_page = @client.get @add_comment_url
|
147
|
+
add_form = add_page.form_with :action => /addcomment/i
|
148
|
+
|
149
|
+
add_form.field_with(:name => /newCommentRaw/i).value = text
|
150
|
+
add_form.field_with(:name => /newComment/i).value = text
|
151
|
+
add_form.checkbox_with(:name => /privateComment/i).checked = :checked
|
152
|
+
if file_data
|
153
|
+
upload = add_form.file_uploads.first
|
154
|
+
upload.file_name = file_name
|
155
|
+
upload.mime_type = file_mime_type
|
156
|
+
upload.file_data = file_data
|
157
|
+
end
|
158
|
+
add_form.submit add_form.button_with(:name => /submit/i)
|
159
|
+
self
|
160
|
+
end
|
161
|
+
|
162
|
+
# Reloads the problem set's comments page.
|
163
|
+
def reload_comments!(page = nil)
|
164
|
+
page ||= @client.get_nokogiri @url
|
165
|
+
@comments = page.css('#comments ~ table.dataTable').map { |table|
|
166
|
+
Comment.new table, self
|
167
|
+
}.reject(&:nil?)
|
168
|
+
end
|
169
|
+
|
170
|
+
# A comment on a Stellar submission.
|
171
|
+
class Comment
|
172
|
+
# Person who posted the comment.
|
173
|
+
attr_reader :author
|
174
|
+
# Comment text.
|
175
|
+
attr_reader :text
|
176
|
+
# URL to the file attached to the comment. Can be nil.
|
177
|
+
attr_reader :attachment_url
|
178
|
+
# True if the comment was deleted.
|
179
|
+
attr_reader :deleted
|
180
|
+
# True if the comment was deleted.
|
181
|
+
alias_method :deleted?, :deleted
|
182
|
+
|
183
|
+
# The submission that the comment was posted on.
|
184
|
+
attr_reader :submission
|
185
|
+
# Generic Stellar client used to make requests.
|
186
|
+
attr_reader :client
|
187
|
+
|
188
|
+
def initialize(table, submission)
|
189
|
+
@submission = submission
|
190
|
+
@client = @submission.client
|
191
|
+
|
192
|
+
page_url = table.document.url
|
193
|
+
@author = table.css('thead tr th.announcedBy').first.inner_text
|
194
|
+
|
195
|
+
unless content = table.css('tbody tr td.announcement').first
|
196
|
+
raise 'Invalid submission comment table'
|
197
|
+
end
|
198
|
+
if (deleted_text = table.css('tbody tr td.announcement > em').first) &&
|
199
|
+
deleted_text.inner_text == 'deleted'
|
200
|
+
@deleted = true
|
201
|
+
@text = nil
|
202
|
+
@attachment_url = nil
|
203
|
+
else
|
204
|
+
@deleted = false
|
205
|
+
|
206
|
+
unless delete_link = table.css('thead a[href*="delete"]').first
|
207
|
+
raise ArgumentError, 'Invalid submission comment table'
|
208
|
+
end
|
209
|
+
@delete_url = URI.join page_url, delete_link['href']
|
210
|
+
@text = content.css('p').inner_text
|
211
|
+
attachment_links = table.css('tbody tr td.announcement > a')
|
212
|
+
if attachment_links.empty?
|
213
|
+
@attachment_url = nil
|
214
|
+
else
|
215
|
+
@attachment_url = URI.join page_url, attachment_links.first['href']
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
# Deletes this comment from Stellar.
|
221
|
+
def delete!
|
222
|
+
return if @deleted
|
223
|
+
delete_page = @client.get @delete_url
|
224
|
+
delete_form = delete_page.form_with(:action => /delete/i)
|
225
|
+
delete_button = delete_form.button_with(:name => /delete/i)
|
226
|
+
delete_form.submit delete_button
|
227
|
+
@deleted = true
|
228
|
+
end
|
229
|
+
|
230
|
+
# The contents of the file attached to this Stellar submission comment.
|
231
|
+
#
|
232
|
+
# @return [String] raw file data
|
233
|
+
def attachment_data
|
234
|
+
@attachment_url && @client.get_file(@attachment_url)
|
235
|
+
end
|
236
|
+
end # class Stellar::Homework::Submission::Comment
|
237
|
+
|
238
|
+
end # class Stellar::Homework::Submission
|
239
|
+
|
240
|
+
end # class Stellar::Homework
|
241
|
+
|
242
|
+
end # namespace Stellar
|
@@ -0,0 +1,21 @@
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
2
|
+
MIIDZTCCAs6gAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJVUzEW
|
3
|
+
MBQGA1UECBMNTWFzc2FjaHVzZXR0czEuMCwGA1UEChMlTWFzc2FjaHVzZXR0cyBJ
|
4
|
+
bnN0aXR1dGUgb2YgVGVjaG5vbG9neTEkMCIGA1UECxMbTUlUIENlcnRpZmljYXRp
|
5
|
+
b24gQXV0aG9yaXR5MB4XDTA2MDQwODE2NTAwNFoXDTI2MDgwMTE2NTAwNFowezEL
|
6
|
+
MAkGA1UEBhMCVVMxFjAUBgNVBAgTDU1hc3NhY2h1c2V0dHMxLjAsBgNVBAoTJU1h
|
7
|
+
c3NhY2h1c2V0dHMgSW5zdGl0dXRlIG9mIFRlY2hub2xvZ3kxJDAiBgNVBAsTG01J
|
8
|
+
VCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCBnzANBgkqhkiG9w0BAQEFAAOBjQAw
|
9
|
+
gYkCgYEA09Dr51G1M3Wm2KOE6gJwXM+cIOALA4uORm4VJeF39mvEcN3UFgvMEYgx
|
10
|
+
OAvufFkkV+mNzXX4UmPdMwzwT5+1/JGuMoWMGnVjGZiGHpIjsofz9cmmopdo8uyy
|
11
|
+
Gq2z9e0J6sznvLRkUBXmVwAaesbe/uEwWFpdq7u0HBHsZMHTpFUCAwEAAaOB+DCB
|
12
|
+
9TAdBgNVHQ4EFgQUU/WjDwZdZdiKj1JtafrrVS29iwwwgaUGA1UdIwSBnTCBmoAU
|
13
|
+
U/WjDwZdZdiKj1JtafrrVS29iwyhf6R9MHsxCzAJBgNVBAYTAlVTMRYwFAYDVQQI
|
14
|
+
Ew1NYXNzYWNodXNldHRzMS4wLAYDVQQKEyVNYXNzYWNodXNldHRzIEluc3RpdHV0
|
15
|
+
ZSBvZiBUZWNobm9sb2d5MSQwIgYDVQQLExtNSVQgQ2VydGlmaWNhdGlvbiBBdXRo
|
16
|
+
b3JpdHmCAQEwDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAQYwEQYJYIZIAYb4QgEB
|
17
|
+
BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4GBAMTjXyVdM89JlPTzoe3o5CIvUP6TrWMN
|
18
|
+
Bm3/mSX5pXeZWbWLtdVfUgQ9mW6UBYXaQSUPmz9C09ZNBH8N3vOoDS5/jD8MMcV/
|
19
|
+
U/rOAIb4v2bMRKpPweSINGm72Pv/Pg15t1sRcnatBK94orekYvfJa3PiPU/3pfge
|
20
|
+
RYhCd9zByXr2
|
21
|
+
-----END CERTIFICATE-----
|
File without changes
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
3
|
+
require 'rspec'
|
4
|
+
require 'stellar'
|
5
|
+
|
6
|
+
# Requires supporting files with custom matchers and macros, etc,
|
7
|
+
# in ./support/ and its subdirectories.
|
8
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
9
|
+
|
10
|
+
RSpec.configure do |config|
|
11
|
+
|
12
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe Stellar::Auth do
|
4
|
+
describe 'get_certificate' do
|
5
|
+
before :all do
|
6
|
+
@result = Stellar::Auth.get_certificate test_mit_kerberos
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'should contain a key pair' do
|
10
|
+
@result[:key].should be_kind_of(OpenSSL::PKey::PKey)
|
11
|
+
@result[:key].should be_private
|
12
|
+
end
|
13
|
+
it 'should contain a certificate' do
|
14
|
+
@result[:cert].should be_kind_of(OpenSSL::X509::Certificate)
|
15
|
+
end
|
16
|
+
it 'should contain a MIT certificate' do
|
17
|
+
@result[:cert].subject.to_s.
|
18
|
+
should match(/O=Massachusetts Institute of Technology/)
|
19
|
+
end
|
20
|
+
it 'should contain a certificate matching the key' do
|
21
|
+
@result[:cert].public_key.to_pem.should == @result[:key].public_key.to_pem
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe 'get_certificate with bad credentials' do
|
26
|
+
it 'should raise ArgumentError' do
|
27
|
+
lambda {
|
28
|
+
Stellar::Auth.get_certificate test_mit_kerberos.merge(:pass => 'fail')
|
29
|
+
}.should raise_error(ArgumentError)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe Stellar::Client do
|
4
|
+
let(:client) { Stellar::Client.new }
|
5
|
+
|
6
|
+
shared_examples_for 'an authenticated client' do
|
7
|
+
describe 'get_nokogiri' do
|
8
|
+
let(:page) { client.get_nokogiri '/atstellar' }
|
9
|
+
|
10
|
+
it 'should be a Nokogiri document' do
|
11
|
+
page.should be_kind_of(Nokogiri::HTML::Document)
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should have course links' do
|
15
|
+
page.css('a[title*="class site"]').length.should > 0
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe '#auth' do
|
21
|
+
describe 'with Kerberos credentials' do
|
22
|
+
before do
|
23
|
+
client.auth :kerberos => test_mit_kerberos
|
24
|
+
end
|
25
|
+
|
26
|
+
it_should_behave_like 'an authenticated client'
|
27
|
+
end
|
28
|
+
|
29
|
+
describe 'with a certificate' do
|
30
|
+
before do
|
31
|
+
client.auth :cert => test_mit_cert
|
32
|
+
end
|
33
|
+
|
34
|
+
it_should_behave_like 'an authenticated client'
|
35
|
+
end
|
36
|
+
|
37
|
+
describe 'with bad Kerberos credentials' do
|
38
|
+
it 'should raise ArgumentError' do
|
39
|
+
client.auth :kerberos => test_mit_kerberos.merge(:pass => 'fail')
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe '#course' do
|
45
|
+
before do
|
46
|
+
client.auth :kerberos => test_mit_kerberos
|
47
|
+
end
|
48
|
+
let(:six) { client.course('6.006', 2011, :fall) }
|
49
|
+
|
50
|
+
it 'should return a Course instance' do
|
51
|
+
six.should be_kind_of(Stellar::Course)
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'should return a 6.006 course' do
|
55
|
+
six.number.should == '6.006'
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|