stellar 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|