lotus 0.0.12

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.
Files changed (48) hide show
  1. data/.gitignore +6 -0
  2. data/.travis.yml +9 -0
  3. data/Gemfile +16 -0
  4. data/README.md +233 -0
  5. data/Rakefile +7 -0
  6. data/lib/lotus.rb +232 -0
  7. data/lib/lotus/activity.rb +134 -0
  8. data/lib/lotus/atom/account.rb +50 -0
  9. data/lib/lotus/atom/address.rb +56 -0
  10. data/lib/lotus/atom/author.rb +167 -0
  11. data/lib/lotus/atom/category.rb +41 -0
  12. data/lib/lotus/atom/entry.rb +159 -0
  13. data/lib/lotus/atom/feed.rb +174 -0
  14. data/lib/lotus/atom/generator.rb +40 -0
  15. data/lib/lotus/atom/link.rb +79 -0
  16. data/lib/lotus/atom/name.rb +57 -0
  17. data/lib/lotus/atom/organization.rb +62 -0
  18. data/lib/lotus/atom/portable_contacts.rb +117 -0
  19. data/lib/lotus/atom/source.rb +168 -0
  20. data/lib/lotus/atom/thread.rb +60 -0
  21. data/lib/lotus/author.rb +177 -0
  22. data/lib/lotus/category.rb +45 -0
  23. data/lib/lotus/crypto.rb +146 -0
  24. data/lib/lotus/feed.rb +190 -0
  25. data/lib/lotus/generator.rb +53 -0
  26. data/lib/lotus/identity.rb +59 -0
  27. data/lib/lotus/link.rb +56 -0
  28. data/lib/lotus/notification.rb +220 -0
  29. data/lib/lotus/publisher.rb +40 -0
  30. data/lib/lotus/subscription.rb +117 -0
  31. data/lib/lotus/version.rb +3 -0
  32. data/lotus.gemspec +27 -0
  33. data/spec/activity_spec.rb +84 -0
  34. data/spec/atom/feed_spec.rb +681 -0
  35. data/spec/author_spec.rb +150 -0
  36. data/spec/crypto_spec.rb +138 -0
  37. data/spec/feed_spec.rb +252 -0
  38. data/spec/helper.rb +8 -0
  39. data/spec/identity_spec.rb +67 -0
  40. data/spec/link_spec.rb +30 -0
  41. data/spec/notification_spec.rb +77 -0
  42. data/test/example_feed.atom +393 -0
  43. data/test/example_feed_empty_author.atom +336 -0
  44. data/test/example_feed_false_connected.atom +359 -0
  45. data/test/example_feed_link_without_href.atom +134 -0
  46. data/test/example_page.html +4 -0
  47. data/test/mime_type_bug_feed.atom +874 -0
  48. metadata +204 -0
@@ -0,0 +1,150 @@
1
+ require_relative 'helper'
2
+ require_relative '../lib/lotus/author.rb'
3
+
4
+ describe Lotus::Author do
5
+ describe "#initialize" do
6
+ it "should store an uri" do
7
+ Lotus::Author.new(:uri => "http://example.com/1").uri.must_equal "http://example.com/1"
8
+ end
9
+
10
+ it "should store a name" do
11
+ Lotus::Author.new(:name => "foo").name.must_equal "foo"
12
+ end
13
+
14
+ it "should store a email" do
15
+ Lotus::Author.new(:email => "foo@example.com").email.must_equal "foo@example.com"
16
+ end
17
+
18
+ it "should store a id" do
19
+ Lotus::Author.new(:id => "1").id.must_equal "1"
20
+ end
21
+
22
+ it "should store a gender" do
23
+ Lotus::Author.new(:gender => "androgynous").gender.must_equal "androgynous"
24
+ end
25
+
26
+ it "should store nickname" do
27
+ Lotus::Author.new(:nickname => "foobar").nickname.must_equal "foobar"
28
+ end
29
+
30
+ it "should store the display name" do
31
+ Lotus::Author.new(:display_name => "foobar").display_name.must_equal "foobar"
32
+ end
33
+
34
+ it "should store the preferred username" do
35
+ Lotus::Author.new(:preferred_username => "foobar")
36
+ .preferred_username.must_equal "foobar"
37
+ end
38
+
39
+ it "should store the birthday" do
40
+ time = mock('datetime')
41
+ Lotus::Author.new(:birthday => time).birthday.must_equal time
42
+ end
43
+
44
+ it "should store the anniversary" do
45
+ time = mock('datetime')
46
+ Lotus::Author.new(:anniversary => time).anniversary.must_equal time
47
+ end
48
+
49
+ it "should store the note" do
50
+ Lotus::Author.new(:note => "note").note.must_equal "note"
51
+ end
52
+
53
+ it "should store the published date" do
54
+ time = mock('datetime')
55
+ Lotus::Author.new(:published => time).published.must_equal time
56
+ end
57
+
58
+ it "should store the updated date" do
59
+ time = mock('datetime')
60
+ Lotus::Author.new(:updated => time).updated.must_equal time
61
+ end
62
+
63
+ it "should store an address hash" do
64
+ address = mock('hash')
65
+ Lotus::Author.new(:address => address).address.must_equal address
66
+ end
67
+
68
+ it "should store an organization hash" do
69
+ organization = mock('hash')
70
+ Lotus::Author.new(:organization => organization).organization.must_equal organization
71
+ end
72
+
73
+ it "should store an extended name hash" do
74
+ name = mock('hash')
75
+ Lotus::Author.new(:extended_name => name).extended_name.must_equal name
76
+ end
77
+
78
+ it "should store an account hash" do
79
+ account = mock('hash')
80
+ Lotus::Author.new(:account => account).account.must_equal account
81
+ end
82
+ end
83
+
84
+ describe "#to_hash" do
85
+ it "should return a Hash containing the id" do
86
+ Lotus::Author.new(:id => "1").to_hash[:id].must_equal "1"
87
+ end
88
+
89
+ it "should return a Hash containing the gender" do
90
+ Lotus::Author.new(:gender => "androgynous").to_hash[:gender].must_equal "androgynous"
91
+ end
92
+
93
+ it "should return a Hash containing nickname" do
94
+ Lotus::Author.new(:nickname => "foobar").to_hash[:nickname].must_equal "foobar"
95
+ end
96
+
97
+ it "should return a Hash containing the display name" do
98
+ Lotus::Author.new(:display_name => "foobar").display_name.must_equal "foobar"
99
+ end
100
+
101
+ it "should return a Hash containing the preferred username" do
102
+ Lotus::Author.new(:preferred_username => "foobar")
103
+ .preferred_username.must_equal "foobar"
104
+ end
105
+
106
+ it "should return a Hash containing the birthday" do
107
+ time = mock('datetime')
108
+ Lotus::Author.new(:birthday => time).to_hash[:birthday].must_equal time
109
+ end
110
+
111
+ it "should return a Hash containing the anniversary" do
112
+ time = mock('datetime')
113
+ Lotus::Author.new(:anniversary => time).to_hash[:anniversary].must_equal time
114
+ end
115
+
116
+ it "should return a Hash containing the note" do
117
+ Lotus::Author.new(:note => "note").to_hash[:note].must_equal "note"
118
+ end
119
+
120
+ it "should return a Hash containing the published date" do
121
+ time = mock('datetime')
122
+ Lotus::Author.new(:published => time).to_hash[:published].must_equal time
123
+ end
124
+
125
+ it "should return a Hash containing the updated date" do
126
+ time = mock('datetime')
127
+ Lotus::Author.new(:updated => time).to_hash[:updated].must_equal time
128
+ end
129
+
130
+ it "should return a Hash containing the address hash" do
131
+ address = mock('hash')
132
+ Lotus::Author.new(:address => address).to_hash[:address].must_equal address
133
+ end
134
+
135
+ it "should return a Hash containing the organization hash" do
136
+ organization = mock('hash')
137
+ Lotus::Author.new(:organization => organization).to_hash[:organization].must_equal organization
138
+ end
139
+
140
+ it "should return a Hash containing the extended name hash" do
141
+ name = mock('hash')
142
+ Lotus::Author.new(:extended_name => name).to_hash[:extended_name].must_equal name
143
+ end
144
+
145
+ it "should return a Hash containing the account hash" do
146
+ account = mock('hash')
147
+ Lotus::Author.new(:account => account).to_hash[:account].must_equal account
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,138 @@
1
+ require_relative 'helper'
2
+ require_relative '../lib/lotus/crypto.rb'
3
+
4
+ describe Lotus::Crypto do
5
+ before do
6
+ key = Struct.new(:modulus, :exponent).new(256, 2)
7
+ key.stubs(:is_a?).returns(true)
8
+
9
+ keypair = Struct.new(:public_key, :private_key).new(key, key)
10
+ keypair.stubs(:decrypt).returns("DECRYPTED")
11
+ keypair.stubs(:encrypt).returns("ENCRYPTED")
12
+
13
+ RSA::KeyPair.stubs(:generate).returns(keypair)
14
+ RSA::KeyPair.stubs(:new).returns(keypair)
15
+
16
+ Base64::stubs(:urlsafe_encode64).returns("Base64")
17
+ Base64::stubs(:urlsafe_decode64).returns("2")
18
+
19
+ RSA::Key.stubs(:new).returns(key)
20
+
21
+ Digest::SHA2.any_instance.stubs(:digest).returns("SHA2")
22
+ end
23
+
24
+ describe "new_keypair" do
25
+ it "should return a KeyPair structure" do
26
+ Lotus::Crypto.new_keypair.class.must_equal Lotus::Crypto::KeyPair
27
+ end
28
+
29
+ it "should return a KeyPair structure with an RSA public key" do
30
+ Lotus::Crypto.new_keypair.public_key.must_match /^RSA\.(.*?)\.(.*)$/
31
+ end
32
+
33
+ it "should return a KeyPair structure with an RSA private key" do
34
+ Lotus::Crypto.new_keypair.private_key.must_match /^RSA\.(.*?)\.(.*)$/
35
+ end
36
+
37
+ it "should relegate to RSA::KeyPair" do
38
+ keypair = RSA::KeyPair.generate
39
+ RSA::KeyPair.expects(:generate).returns(keypair)
40
+ Lotus::Crypto.new_keypair
41
+ end
42
+ end
43
+
44
+ describe "emsa_sign" do
45
+ it "should return a string with the EMSA prefix" do
46
+ keypair = Lotus::Crypto.new_keypair
47
+
48
+ sequence = "^\x00\x01\x00\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20"
49
+ matcher = Regexp.new(sequence, nil, 'n')
50
+
51
+ RSA::KeyPair.new.expects(:decrypt).with(regexp_matches(matcher))
52
+ Lotus::Crypto.emsa_sign "payload", keypair.private_key
53
+ end
54
+
55
+ it "should return the result of decryption with the private key" do
56
+ keypair = Lotus::Crypto.new_keypair
57
+ Lotus::Crypto.emsa_sign("payload", keypair.private_key)
58
+ .must_equal "DECRYPTED"
59
+ end
60
+
61
+ it "should end the signature with the SHA2 of the plaintext" do
62
+ keypair = Lotus::Crypto.new_keypair
63
+
64
+ Digest::SHA2.any_instance.expects(:digest)
65
+ .with("payload")
66
+ .returns("SHA2")
67
+
68
+ matcher = /\x20SHA2$/
69
+ RSA::KeyPair.new.expects(:decrypt).with(regexp_matches(matcher))
70
+
71
+ Lotus::Crypto.emsa_sign("payload", keypair.private_key)
72
+ end
73
+ end
74
+
75
+ describe "emsa_verify" do
76
+ it "should return true when the message matches" do
77
+ keypair = Lotus::Crypto.new_keypair
78
+
79
+ valid_signature =
80
+ "\x00\x01\x00\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20SHA2"
81
+
82
+ valid_signature.force_encoding('binary')
83
+
84
+ signature = Lotus::Crypto.emsa_sign("payload", keypair.private_key)
85
+
86
+ RSA::KeyPair.new.expects(:encrypt)
87
+ .with(signature)
88
+ .returns(valid_signature)
89
+
90
+ Lotus::Crypto.emsa_verify("payload", signature, keypair.public_key)
91
+ .must_equal true
92
+ end
93
+
94
+ it "should return false when the message does not match" do
95
+ keypair = Lotus::Crypto.new_keypair
96
+
97
+ bogus_signature =
98
+ "\x00\x01\x00\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20BOGUS"
99
+
100
+ bogus_signature.force_encoding('binary')
101
+
102
+ signature = Lotus::Crypto.emsa_sign("payload", keypair.private_key)
103
+
104
+ RSA::KeyPair.new.expects(:encrypt)
105
+ .with(signature)
106
+ .returns(bogus_signature)
107
+
108
+ Lotus::Crypto.emsa_verify("payload", signature, keypair.public_key)
109
+ .must_equal false
110
+ end
111
+ end
112
+
113
+ describe "decrypt" do
114
+ it "should relegate to RSA::KeyPair" do
115
+ keypair = Lotus::Crypto.new_keypair
116
+
117
+ RSA::KeyPair.new.expects(:decrypt)
118
+ .with("payload")
119
+ .returns("OBSCURED")
120
+
121
+ Lotus::Crypto.decrypt(keypair.private_key, "payload")
122
+ .must_equal "OBSCURED"
123
+ end
124
+ end
125
+
126
+ describe "encrypt" do
127
+ it "should relegate to RSA::KeyPair" do
128
+ keypair = Lotus::Crypto.new_keypair
129
+
130
+ RSA::KeyPair.new.expects(:encrypt)
131
+ .with("payload")
132
+ .returns("OBSCURED")
133
+
134
+ Lotus::Crypto.encrypt(keypair.public_key, "payload")
135
+ .must_equal "OBSCURED"
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,252 @@
1
+ require_relative 'helper'
2
+ require_relative '../lib/lotus/feed.rb'
3
+
4
+ describe Lotus::Feed do
5
+ describe "#initialize" do
6
+ it "should store a title" do
7
+ Lotus::Feed.new(:title => "My Title").title.must_equal "My Title"
8
+ end
9
+
10
+ it "should store a title type" do
11
+ Lotus::Feed.new(:title_type => "html").title_type.must_equal "html"
12
+ end
13
+
14
+ it "should store a subtitle" do
15
+ Lotus::Feed.new(:subtitle => "My Title").subtitle.must_equal "My Title"
16
+ end
17
+
18
+ it "should store a subtitle type" do
19
+ Lotus::Feed.new(:subtitle_type => "html").subtitle_type.must_equal "html"
20
+ end
21
+
22
+ it "should store a list of categories" do
23
+ category = mock('category')
24
+ Lotus::Feed.new(:categories => [category]).categories.must_equal [category]
25
+ end
26
+
27
+ it "should store a list of authors" do
28
+ author = mock('author')
29
+ Lotus::Feed.new(:authors => [author]).authors.must_equal [author]
30
+ end
31
+
32
+ it "should store a list of contributors" do
33
+ author = mock('author')
34
+ Lotus::Feed.new(:contributors => [author]).contributors.must_equal [author]
35
+ end
36
+
37
+ it "should store a list of entries" do
38
+ entry = mock('entry')
39
+ Lotus::Feed.new(:entries => [entry]).entries.must_equal [entry]
40
+ end
41
+
42
+ it "should store a list of hubs" do
43
+ Lotus::Feed.new(:hubs => ["http://hub.example.com"]).hubs
44
+ .must_equal ["http://hub.example.com"]
45
+ end
46
+
47
+ it "should store the id of the feed" do
48
+ Lotus::Feed.new(:id => "id").id.must_equal "id"
49
+ end
50
+
51
+ it "should store the icon for the feed" do
52
+ Lotus::Feed.new(:icon => "url").icon.must_equal "url"
53
+ end
54
+
55
+ it "should store the logo for the feed" do
56
+ Lotus::Feed.new(:logo => "url").logo.must_equal "url"
57
+ end
58
+
59
+ it "should store the rights for the feed" do
60
+ Lotus::Feed.new(:rights => "url").rights.must_equal "url"
61
+ end
62
+
63
+ it "should store the url for the feed" do
64
+ Lotus::Feed.new(:url => "url").url.must_equal "url"
65
+ end
66
+
67
+ it "should store the published date" do
68
+ time = mock('date')
69
+ Lotus::Feed.new(:published => time).published.must_equal time
70
+ end
71
+
72
+ it "should store the updated date" do
73
+ time = mock('date')
74
+ Lotus::Feed.new(:updated => time).updated.must_equal time
75
+ end
76
+
77
+ it "should store the salmon url for the feed" do
78
+ Lotus::Feed.new(:salmon_url => "url").salmon_url.must_equal "url"
79
+ end
80
+
81
+ it "should store the generator for the feed" do
82
+ Lotus::Feed.new(:generator => "feedmaker").generator.must_equal "feedmaker"
83
+ end
84
+
85
+ it "should yield an empty array for categories by default" do
86
+ Lotus::Feed.new.categories.must_equal []
87
+ end
88
+
89
+ it "should yield an empty array for authors by default" do
90
+ Lotus::Feed.new.authors.must_equal []
91
+ end
92
+
93
+ it "should yield an empty array for contributors by default" do
94
+ Lotus::Feed.new.contributors.must_equal []
95
+ end
96
+
97
+ it "should yield an empty array for entries by default" do
98
+ Lotus::Feed.new.entries.must_equal []
99
+ end
100
+
101
+ it "should yield an empty array for hubs by default" do
102
+ Lotus::Feed.new.hubs.must_equal []
103
+ end
104
+
105
+ it "should yield a title of 'Untitled' by default" do
106
+ Lotus::Feed.new.title.must_equal "Untitled"
107
+ end
108
+ end
109
+
110
+ describe "#to_link" do
111
+ it "should return a Lotus::Link" do
112
+ link = mock('link')
113
+ Lotus::Link.stubs(:new).returns(link)
114
+ Lotus::Feed.new.to_link.must_equal link
115
+ end
116
+
117
+ it "should by default use the title of the feed" do
118
+ Lotus::Link.expects(:new).with(has_entry(:title, "title"))
119
+ Lotus::Feed.new(:title => "title").to_link
120
+ end
121
+
122
+ it "should by default use the url of the feed as the href" do
123
+ Lotus::Link.expects(:new).with(has_entry(:href, "http://example.com"))
124
+ Lotus::Feed.new(:url => "http://example.com").to_link
125
+ end
126
+
127
+ it "should override the title of the feed when given" do
128
+ Lotus::Link.expects(:new).with(has_entry(:title, "new title"))
129
+ Lotus::Feed.new(:title => "title").to_link(:title => "new title")
130
+ end
131
+
132
+ it "should override the url of the feed when given" do
133
+ Lotus::Link.expects(:new).with(has_entry(:url, "http://feeds.example.com"))
134
+ Lotus::Feed.new(:url => "http://example.com")
135
+ .to_link(:url => "http://feeds.example.com")
136
+ end
137
+
138
+ it "should pass through the rel option" do
139
+ Lotus::Link.expects(:new).with(has_entry(:rel, "alternate"))
140
+ Lotus::Feed.new.to_link(:rel => "alternate")
141
+ end
142
+
143
+ it "should pass through the hreflang option" do
144
+ Lotus::Link.expects(:new).with(has_entry(:hreflang, "en_us"))
145
+ Lotus::Feed.new.to_link(:hreflang => "en_us")
146
+ end
147
+
148
+ it "should pass through the length option" do
149
+ Lotus::Link.expects(:new).with(has_entry(:length, 12345))
150
+ Lotus::Feed.new.to_link(:length => 12345)
151
+ end
152
+
153
+ it "should pass through the type option" do
154
+ Lotus::Link.expects(:new).with(has_entry(:type, "html"))
155
+ Lotus::Feed.new.to_link(:type => "html")
156
+ end
157
+ end
158
+
159
+ describe "#to_hash" do
160
+ it "should return a Hash containing the title" do
161
+ Lotus::Feed.new(:title => "My Title").to_hash[:title].must_equal "My Title"
162
+ end
163
+
164
+ it "should return a Hash containing the title type" do
165
+ Lotus::Feed.new(:title_type => "html").to_hash[:title_type].must_equal "html"
166
+ end
167
+
168
+ it "should return a Hash containing the subtitle" do
169
+ Lotus::Feed.new(:subtitle => "My Title").to_hash[:subtitle].must_equal "My Title"
170
+ end
171
+
172
+ it "should return a Hash containing the subtitle type" do
173
+ Lotus::Feed.new(:subtitle_type => "html").to_hash[:subtitle_type].must_equal "html"
174
+ end
175
+
176
+ it "should return a Hash containing a list of categories" do
177
+ category = mock('category')
178
+ Lotus::Feed.new(:categories => [category]).to_hash[:categories].must_equal [category]
179
+ end
180
+
181
+ it "should return a Hash containing a list of authors" do
182
+ author = mock('author')
183
+ Lotus::Feed.new(:authors => [author]).to_hash[:authors].must_equal [author]
184
+ end
185
+
186
+ it "should return a Hash containing a list of contributors" do
187
+ author = mock('author')
188
+ Lotus::Feed.new(:contributors => [author]).to_hash[:contributors].must_equal [author]
189
+ end
190
+
191
+ it "should return a Hash containing a list of entries" do
192
+ entry = mock('entry')
193
+ Lotus::Feed.new(:entries => [entry]).to_hash[:entries].must_equal [entry]
194
+ end
195
+
196
+ it "should return a Hash containing a list of hubs" do
197
+ Lotus::Feed.new(:hubs => ["hub1", "hub2"]).to_hash[:hubs]
198
+ .must_equal ["hub1", "hub2"]
199
+ end
200
+
201
+ it "should return a Hash containing the id of the feed" do
202
+ Lotus::Feed.new(:id => "id").to_hash[:id].must_equal "id"
203
+ end
204
+
205
+ it "should return a Hash containing the icon for the feed" do
206
+ Lotus::Feed.new(:icon => "url").to_hash[:icon].must_equal "url"
207
+ end
208
+
209
+ it "should return a Hash containing the logo for the feed" do
210
+ Lotus::Feed.new(:logo => "url").to_hash[:logo].must_equal "url"
211
+ end
212
+
213
+ it "should return a Hash containing the rights for the feed" do
214
+ Lotus::Feed.new(:rights => "CC0").to_hash[:rights].must_equal "CC0"
215
+ end
216
+
217
+ it "should return a Hash containing the url for the feed" do
218
+ Lotus::Feed.new(:url => "url").to_hash[:url].must_equal "url"
219
+ end
220
+
221
+ it "should return a Hash containing the salmon url for the feed" do
222
+ Lotus::Feed.new(:salmon_url => "url").to_hash[:salmon_url].must_equal "url"
223
+ end
224
+
225
+ it "should return a Hash containing the published date" do
226
+ time = mock('date')
227
+ Lotus::Feed.new(:published => time).to_hash[:published].must_equal time
228
+ end
229
+
230
+ it "should return a Hash containing the updated date" do
231
+ time = mock('date')
232
+ Lotus::Feed.new(:updated => time).to_hash[:updated].must_equal time
233
+ end
234
+
235
+ it "should return a Hash containing the generator" do
236
+ Lotus::Feed.new(:generator => "feedmaker").to_hash[:generator].must_equal "feedmaker"
237
+ end
238
+ end
239
+
240
+ describe "#to_atom" do
241
+ it "should relegate Atom generation to Lotus::Atom::Feed" do
242
+ atom_entry = mock('atom')
243
+ atom_entry.expects(:to_xml).returns("ATOM")
244
+
245
+ require_relative '../lib/lotus/atom/feed.rb'
246
+
247
+ Lotus::Atom::Feed.stubs(:new).returns(atom_entry)
248
+
249
+ Lotus::Feed.new(:title => "foo").to_atom.must_equal "ATOM"
250
+ end
251
+ end
252
+ end