meteor-motion 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +21 -0
  3. data/.repl_history +0 -0
  4. data/Gemfile +6 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +166 -0
  7. data/Rakefile +17 -0
  8. data/app/app_delegate.rb +11 -0
  9. data/app/controllers/book_controller.rb +92 -0
  10. data/app/controllers/book_list_controller.rb +105 -0
  11. data/app/controllers/connection_controller.rb +83 -0
  12. data/app/controllers/login_controller.rb +35 -0
  13. data/lib/meteor-motion.rb +12 -0
  14. data/lib/meteor-motion/version.rb +3 -0
  15. data/meteor-motion.gemspec +27 -0
  16. data/motion/adapters/motion_model.rb +61 -0
  17. data/motion/client.rb +179 -0
  18. data/motion/collection.rb +50 -0
  19. data/motion/collections/default.rb +56 -0
  20. data/motion/collections/motion_model.rb +52 -0
  21. data/motion/ddp.rb +161 -0
  22. data/motion/srp/securerandom.rb +248 -0
  23. data/motion/srp/srp.rb +250 -0
  24. data/spec/adapters/motion_model_spec.rb +38 -0
  25. data/spec/client_spec.rb +104 -0
  26. data/spec/collection_spec.rb +63 -0
  27. data/spec/collections/default_spec.rb +46 -0
  28. data/spec/collections/motion_model_spec.rb +69 -0
  29. data/spec/ddp_spec.rb +123 -0
  30. data/spec/server/.meteor/.gitignore +1 -0
  31. data/spec/server/.meteor/packages +9 -0
  32. data/spec/server/.meteor/release +1 -0
  33. data/spec/server/collections/books.js +11 -0
  34. data/spec/server/server/fixtures.js +28 -0
  35. data/spec/server/server/publications.js +3 -0
  36. data/spec/server/smart.json +3 -0
  37. data/vendor/SocketRocket/NSData+SRB64Additions.h +24 -0
  38. data/vendor/SocketRocket/NSData+SRB64Additions.m +39 -0
  39. data/vendor/SocketRocket/SRWebSocket.h +114 -0
  40. data/vendor/SocketRocket/SRWebSocket.m +1757 -0
  41. data/vendor/SocketRocket/SocketRocket-Prefix.pch +27 -0
  42. data/vendor/SocketRocket/SocketRocket.bridgesupport +160 -0
  43. data/vendor/SocketRocket/base64.c +314 -0
  44. data/vendor/SocketRocket/base64.h +34 -0
  45. metadata +190 -0
@@ -0,0 +1,250 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ module SRP
4
+ class << self
5
+
6
+ def sha256(s)
7
+ RmDigest::SHA256.hexdigest(s)
8
+ end
9
+
10
+ def bigrand(bytes)
11
+ # Replaced with local code lifted from MacRuby
12
+ SecureRandom.random_bytes(bytes).unpack("H*")[0]
13
+ end
14
+
15
+ # a^n (mod m)
16
+ def modpow(a, n, m)
17
+ r = 1
18
+ while true
19
+ r = r * a % m if n[0] == 1
20
+ n >>= 1
21
+ return r if n == 0
22
+ a = a * a % m
23
+ end
24
+ end
25
+
26
+ def Ng(group)
27
+ case group
28
+ when 1024
29
+ @N = %w{
30
+ EEAF0AB9 ADB38DD6 9C33F80A FA8FC5E8 60726187 75FF3C0B 9EA2314C
31
+ 9C256576 D674DF74 96EA81D3 383B4813 D692C6E0 E0D5D8E2 50B98BE4
32
+ 8E495C1D 6089DAD1 5DC7D7B4 6154D6B6 CE8EF4AD 69B15D49 82559B29
33
+ 7BCF1885 C529F566 660E57EC 68EDBC3C 05726CC0 2FD4CBF4 976EAA9A
34
+ FD5138FE 8376435B 9FC61D2F C0EB06E3
35
+ }.join.hex
36
+ @g = 2
37
+
38
+ when 1536
39
+ @N = %w{
40
+ 9DEF3CAF B939277A B1F12A86 17A47BBB DBA51DF4 99AC4C80 BEEEA961
41
+ 4B19CC4D 5F4F5F55 6E27CBDE 51C6A94B E4607A29 1558903B A0D0F843
42
+ 80B655BB 9A22E8DC DF028A7C EC67F0D0 8134B1C8 B9798914 9B609E0B
43
+ E3BAB63D 47548381 DBC5B1FC 764E3F4B 53DD9DA1 158BFD3E 2B9C8CF5
44
+ 6EDF0195 39349627 DB2FD53D 24B7C486 65772E43 7D6C7F8C E442734A
45
+ F7CCB7AE 837C264A E3A9BEB8 7F8A2FE9 B8B5292E 5A021FFF 5E91479E
46
+ 8CE7A28C 2442C6F3 15180F93 499A234D CF76E3FE D135F9BB
47
+ }.join.hex
48
+ @g = 2
49
+
50
+ when 2048
51
+ @N = %w{
52
+ AC6BDB41 324A9A9B F166DE5E 1389582F AF72B665 1987EE07 FC319294
53
+ 3DB56050 A37329CB B4A099ED 8193E075 7767A13D D52312AB 4B03310D
54
+ CD7F48A9 DA04FD50 E8083969 EDB767B0 CF609517 9A163AB3 661A05FB
55
+ D5FAAAE8 2918A996 2F0B93B8 55F97993 EC975EEA A80D740A DBF4FF74
56
+ 7359D041 D5C33EA7 1D281E44 6B14773B CA97B43A 23FB8016 76BD207A
57
+ 436C6481 F1D2B907 8717461A 5B9D32E6 88F87748 544523B5 24B0D57D
58
+ 5EA77A27 75D2ECFA 032CFBDB F52FB378 61602790 04E57AE6 AF874E73
59
+ 03CE5329 9CCC041C 7BC308D8 2A5698F3 A8D0C382 71AE35F8 E9DBFBB6
60
+ 94B5C803 D89F7AE4 35DE236D 525F5475 9B65E372 FCD68EF2 0FA7111F
61
+ 9E4AFF73
62
+ }.join.hex
63
+ @g = 2
64
+
65
+ when 3072
66
+ @N = %w{
67
+ FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 29024E08
68
+ 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD EF9519B3 CD3A431B
69
+ 302B0A6D F25F1437 4FE1356D 6D51C245 E485B576 625E7EC6 F44C42E9
70
+ A637ED6B 0BFF5CB6 F406B7ED EE386BFB 5A899FA5 AE9F2411 7C4B1FE6
71
+ 49286651 ECE45B3D C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8
72
+ FD24CF5F 83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D
73
+ 670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B E39E772C
74
+ 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9 DE2BCBF6 95581718
75
+ 3995497C EA956AE5 15D22618 98FA0510 15728E5A 8AAAC42D AD33170D
76
+ 04507A33 A85521AB DF1CBA64 ECFB8504 58DBEF0A 8AEA7157 5D060C7D
77
+ B3970F85 A6E1E4C7 ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226
78
+ 1AD2EE6B F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C
79
+ BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31 43DB5BFC
80
+ E0FD108E 4B82D120 A93AD2CA FFFFFFFF FFFFFFFF
81
+ }.join.hex
82
+ @g = 5
83
+
84
+ when 4096
85
+ @N = %w{
86
+ FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 29024E08
87
+ 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD EF9519B3 CD3A431B
88
+ 302B0A6D F25F1437 4FE1356D 6D51C245 E485B576 625E7EC6 F44C42E9
89
+ A637ED6B 0BFF5CB6 F406B7ED EE386BFB 5A899FA5 AE9F2411 7C4B1FE6
90
+ 49286651 ECE45B3D C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8
91
+ FD24CF5F 83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D
92
+ 670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B E39E772C
93
+ 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9 DE2BCBF6 95581718
94
+ 3995497C EA956AE5 15D22618 98FA0510 15728E5A 8AAAC42D AD33170D
95
+ 04507A33 A85521AB DF1CBA64 ECFB8504 58DBEF0A 8AEA7157 5D060C7D
96
+ B3970F85 A6E1E4C7 ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226
97
+ 1AD2EE6B F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C
98
+ BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31 43DB5BFC
99
+ E0FD108E 4B82D120 A9210801 1A723C12 A787E6D7 88719A10 BDBA5B26
100
+ 99C32718 6AF4E23C 1A946834 B6150BDA 2583E9CA 2AD44CE8 DBBBC2DB
101
+ 04DE8EF9 2E8EFC14 1FBECAA6 287C5947 4E6BC05D 99B2964F A090C3A2
102
+ 233BA186 515BE7ED 1F612970 CEE2D7AF B81BDD76 2170481C D0069127
103
+ D5B05AA9 93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34063199
104
+ FFFFFFFF FFFFFFFF
105
+ }.join.hex
106
+ @g = 5
107
+
108
+ when 6144
109
+ @N = %w{
110
+ FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 29024E08
111
+ 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD EF9519B3 CD3A431B
112
+ 302B0A6D F25F1437 4FE1356D 6D51C245 E485B576 625E7EC6 F44C42E9
113
+ A637ED6B 0BFF5CB6 F406B7ED EE386BFB 5A899FA5 AE9F2411 7C4B1FE6
114
+ 49286651 ECE45B3D C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8
115
+ FD24CF5F 83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D
116
+ 670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B E39E772C
117
+ 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9 DE2BCBF6 95581718
118
+ 3995497C EA956AE5 15D22618 98FA0510 15728E5A 8AAAC42D AD33170D
119
+ 04507A33 A85521AB DF1CBA64 ECFB8504 58DBEF0A 8AEA7157 5D060C7D
120
+ B3970F85 A6E1E4C7 ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226
121
+ 1AD2EE6B F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C
122
+ BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31 43DB5BFC
123
+ E0FD108E 4B82D120 A9210801 1A723C12 A787E6D7 88719A10 BDBA5B26
124
+ 99C32718 6AF4E23C 1A946834 B6150BDA 2583E9CA 2AD44CE8 DBBBC2DB
125
+ 04DE8EF9 2E8EFC14 1FBECAA6 287C5947 4E6BC05D 99B2964F A090C3A2
126
+ 233BA186 515BE7ED 1F612970 CEE2D7AF B81BDD76 2170481C D0069127
127
+ D5B05AA9 93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34028492
128
+ 36C3FAB4 D27C7026 C1D4DCB2 602646DE C9751E76 3DBA37BD F8FF9406
129
+ AD9E530E E5DB382F 413001AE B06A53ED 9027D831 179727B0 865A8918
130
+ DA3EDBEB CF9B14ED 44CE6CBA CED4BB1B DB7F1447 E6CC254B 33205151
131
+ 2BD7AF42 6FB8F401 378CD2BF 5983CA01 C64B92EC F032EA15 D1721D03
132
+ F482D7CE 6E74FEF6 D55E702F 46980C82 B5A84031 900B1C9E 59E7C97F
133
+ BEC7E8F3 23A97A7E 36CC88BE 0F1D45B7 FF585AC5 4BD407B2 2B4154AA
134
+ CC8F6D7E BF48E1D8 14CC5ED2 0F8037E0 A79715EE F29BE328 06A1D58B
135
+ B7C5DA76 F550AA3D 8A1FBFF0 EB19CCB1 A313D55C DA56C9EC 2EF29632
136
+ 387FE8D7 6E3C0468 043E8F66 3F4860EE 12BF2D5B 0B7474D6 E694F91E
137
+ 6DCC4024 FFFFFFFF FFFFFFFF
138
+ }.join.hex
139
+ @g = 5
140
+
141
+ when 8192
142
+ @N = %w{
143
+ FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 29024E08
144
+ 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD EF9519B3 CD3A431B
145
+ 302B0A6D F25F1437 4FE1356D 6D51C245 E485B576 625E7EC6 F44C42E9
146
+ A637ED6B 0BFF5CB6 F406B7ED EE386BFB 5A899FA5 AE9F2411 7C4B1FE6
147
+ 49286651 ECE45B3D C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8
148
+ FD24CF5F 83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D
149
+ 670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B E39E772C
150
+ 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9 DE2BCBF6 95581718
151
+ 3995497C EA956AE5 15D22618 98FA0510 15728E5A 8AAAC42D AD33170D
152
+ 04507A33 A85521AB DF1CBA64 ECFB8504 58DBEF0A 8AEA7157 5D060C7D
153
+ B3970F85 A6E1E4C7 ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226
154
+ 1AD2EE6B F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C
155
+ BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31 43DB5BFC
156
+ E0FD108E 4B82D120 A9210801 1A723C12 A787E6D7 88719A10 BDBA5B26
157
+ 99C32718 6AF4E23C 1A946834 B6150BDA 2583E9CA 2AD44CE8 DBBBC2DB
158
+ 04DE8EF9 2E8EFC14 1FBECAA6 287C5947 4E6BC05D 99B2964F A090C3A2
159
+ 233BA186 515BE7ED 1F612970 CEE2D7AF B81BDD76 2170481C D0069127
160
+ D5B05AA9 93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34028492
161
+ 36C3FAB4 D27C7026 C1D4DCB2 602646DE C9751E76 3DBA37BD F8FF9406
162
+ AD9E530E E5DB382F 413001AE B06A53ED 9027D831 179727B0 865A8918
163
+ DA3EDBEB CF9B14ED 44CE6CBA CED4BB1B DB7F1447 E6CC254B 33205151
164
+ 2BD7AF42 6FB8F401 378CD2BF 5983CA01 C64B92EC F032EA15 D1721D03
165
+ F482D7CE 6E74FEF6 D55E702F 46980C82 B5A84031 900B1C9E 59E7C97F
166
+ BEC7E8F3 23A97A7E 36CC88BE 0F1D45B7 FF585AC5 4BD407B2 2B4154AA
167
+ CC8F6D7E BF48E1D8 14CC5ED2 0F8037E0 A79715EE F29BE328 06A1D58B
168
+ B7C5DA76 F550AA3D 8A1FBFF0 EB19CCB1 A313D55C DA56C9EC 2EF29632
169
+ 387FE8D7 6E3C0468 043E8F66 3F4860EE 12BF2D5B 0B7474D6 E694F91E
170
+ 6DBE1159 74A3926F 12FEE5E4 38777CB6 A932DF8C D8BEC4D0 73B931BA
171
+ 3BC832B6 8D9DD300 741FA7BF 8AFC47ED 2576F693 6BA42466 3AAB639C
172
+ 5AE4F568 3423B474 2BF1C978 238F16CB E39D652D E3FDB8BE FC848AD9
173
+ 22222E04 A4037C07 13EB57A8 1A23F0C7 3473FC64 6CEA306B 4BCBC886
174
+ 2F8385DD FA9D4B7F A2C087E8 79683303 ED5BDD3A 062B3CF5 B3A278A6
175
+ 6D2A13F8 3F44F82D DF310EE0 74AB6A36 4597E899 A0255DC1 64F31CC5
176
+ 0846851D F9AB4819 5DED7EA1 B1D510BD 7EE74D73 FAF36BC3 1ECFA268
177
+ 359046F4 EB879F92 4009438B 481C6CD7 889A002E D5EE382B C9190DA6
178
+ FC026E47 9558E447 5677E9AA 9E3050E2 765694DF C81F56E8 80B96E71
179
+ 60C980DD 98EDD3DF FFFFFFFF FFFFFFFF
180
+ }.join.hex
181
+ @g = 19
182
+
183
+ else
184
+ raise NotImplementedError
185
+ end
186
+ return [@N, @g]
187
+ end
188
+ end
189
+
190
+
191
+ class Client
192
+ attr_reader :N, :g, :k, :a, :A, :S, :K, :M, :HAMK
193
+
194
+ def initialize group=1024
195
+ # select modulus (N) and generator (g)
196
+ @N, @g = SRP.Ng group
197
+ @k = SRP.sha256( @N.to_s(16) + @g.to_s(16) ).hex
198
+ end
199
+
200
+ def start_authentication
201
+ while !@A || (@A % @N == 0 ) do
202
+ @a = random_bignum
203
+ @A = SRP.modpow(@g, @a, @N)
204
+ end
205
+
206
+ return @A.to_s(16)
207
+ end
208
+
209
+ # Process initiated authentication challenge.
210
+ # Returns M if authentication is successful, false otherwise.
211
+ # Implemented based on client definition in Meteor source
212
+ # Note: This varies a bit form the SRP def
213
+ #
214
+ def process_challenge identity, password, salt_str, b_str
215
+
216
+ b_int = b_str.to_i(16)
217
+ # SRP-6a safety check
218
+ return false if (b_int % @N) == 0
219
+
220
+ u = SRP.sha256( @A.to_s(16) + b_str ).hex
221
+ x = SRP.sha256( salt_str + SRP.sha256( identity + ":" + password ) ).hex
222
+
223
+ # SRP-6a safety check
224
+ return false if u == 0
225
+
226
+ # calculate session key
227
+ kgx = k * SRP.modpow(@g, x, @N)
228
+ aux = @a + ( u * x )
229
+ @S = SRP.modpow( b_int - kgx, aux, @N)
230
+
231
+ # calculate match
232
+ @M = SRP.sha256( @A.to_s(16) + b_str + @S.to_s(16) )
233
+
234
+ # calculate verifier
235
+ @HAMK = SRP.sha256( @A.to_s(16) + @M + @S.to_s(16) )
236
+
237
+ return @M
238
+ end
239
+
240
+ def verify server_HAMK
241
+ return false unless @HAMK
242
+ @HAMK == server_HAMK
243
+ end
244
+
245
+ def random_bignum
246
+ SRP.bigrand(32).hex
247
+ end
248
+
249
+ end # Client
250
+ end
@@ -0,0 +1,38 @@
1
+ class Books
2
+ include MotionModel::Model
3
+ include MotionModel::ArrayModelAdapter
4
+ include MeteorMotion::Adapters::MotionModel
5
+
6
+
7
+ # Make sure to define :id as String so MotionModel does not create a default int
8
+ #
9
+ columns :id => :string,
10
+ :title => :string,
11
+ :author => :string,
12
+ :year => :string
13
+ end
14
+
15
+ describe MeteorMotion::Adapters::MotionModel do
16
+
17
+ before do
18
+ @client = MeteorMotion::Client.new
19
+ @client.connect
20
+ @client.add_collection(Books)
21
+ end
22
+
23
+ it 'should successfully populate a collection with data from the server' do
24
+ wait 1.0 do
25
+ @client.subscribe('books')
26
+ end
27
+
28
+ wait 2.0 do
29
+ Books.all.count.should.be.equal 3
30
+
31
+ book = Books.all.first
32
+ book.title.should.be.equal "Foundation"
33
+ book.author.should.be.equal "Isaac Asimov"
34
+ book.year.should.be.equal "1951"
35
+ end
36
+ end
37
+
38
+ end
@@ -0,0 +1,104 @@
1
+ describe MeteorMotion::Client do
2
+
3
+ before do
4
+ @client = MeteorMotion::Client.new
5
+ @client.connect
6
+ wait 1.5 do
7
+ end
8
+ end
9
+
10
+ describe 'Collections' do
11
+ it 'adds and removes collections' do
12
+ @client.add_collection('books')
13
+ @client.collections['books'].should.not.be.equal nil
14
+
15
+ @client.remove_collection('books')
16
+ @client.collections['books'].should.be.equal nil
17
+ end
18
+ end
19
+
20
+ describe 'Subscriptions' do
21
+ it 'adds and removes subscriptions' do
22
+ id = @client.subscribe('books')
23
+ @client.subscriptions[id].should.not.be.equal nil
24
+
25
+ wait 1.0 do
26
+ @client.unsubscribe(id)
27
+ @client.subscriptions[id].should.be.equal nil
28
+ end
29
+ end
30
+ end
31
+
32
+ describe 'Method calls' do
33
+
34
+ def handler action, result
35
+ @called << action
36
+ end
37
+
38
+ before do
39
+ @called = []
40
+ end
41
+
42
+ it 'calls the specified callback when called' do
43
+ @client.call('ping', self.method(:handler) )
44
+
45
+ wait 1.0 do
46
+ @called.include?(:result).should.be.equal true
47
+ end
48
+ end
49
+
50
+ it 'calls the provided handler with an error when a call fails' do
51
+ @client.call('some_method', self.method(:handler) )
52
+
53
+ wait 1.0 do
54
+ @called.include?(:error).should.be.equal true
55
+ end
56
+ end
57
+
58
+ it 'removes a callback after both replies have been received' do
59
+ @client.call('ping', self.method(:handler) )
60
+
61
+ @client.method_callbacks.size.should.be.equal 1
62
+
63
+ wait 1.0 do
64
+ @client.method_callbacks.size.should.be.equal 0
65
+ end
66
+ end
67
+ end
68
+
69
+ describe 'Authentication with username' do
70
+
71
+ def login_handler action, result
72
+ @action = action
73
+ @result = result
74
+ end
75
+
76
+ it 'logins successfully with a correct u/p combo' do
77
+ @client.login_with_username('user', 'pass', self.method(:login_handler))
78
+
79
+ wait 3.0 do
80
+ @action.should == :success
81
+ end
82
+ end
83
+
84
+ it 'fails to login with an invalid username' do
85
+ @client.login_with_username('no_such_user', 'pass', self.method(:login_handler))
86
+
87
+ wait 2.0 do
88
+ @action.should == :error
89
+ @result[:reason].should == 'User not found'
90
+ end
91
+ end
92
+
93
+ it 'fails to login with an incorrect password' do
94
+ @client.login_with_username('user', 'wrong_pass', self.method(:login_handler))
95
+
96
+ wait 3.0 do
97
+ @action.should == :error
98
+ @result[:reason].should == 'Incorrect password'
99
+ end
100
+ end
101
+
102
+ end
103
+
104
+ end
@@ -0,0 +1,63 @@
1
+ describe MeteorMotion::Collection do
2
+
3
+ describe 'creating' do
4
+ it 'creates a new collection with a name' do
5
+ coll = MeteorMotion::Collection.new('temp')
6
+
7
+ coll.name.should.be.equal 'temp'
8
+ end
9
+ end
10
+
11
+ describe 'observers' do
12
+
13
+ def collection_handler action, id
14
+ @action = action
15
+ end
16
+
17
+ before do
18
+ @coll = MeteorMotion::Collection.new('temp')
19
+ end
20
+
21
+ it 'adds an observer to the collection' do
22
+ @coll.add_observer( self.method(:collection_handler) )
23
+ @coll.observers.size.should.be.equal 1
24
+ end
25
+
26
+ it 'notifies a subscribed observer after adding an item' do
27
+ @coll.add_observer( self.method(:collection_handler) )
28
+ @coll.add('abc', {a: 1, b:2})
29
+
30
+ wait 1.0 do
31
+ @action.should.be.equal :added
32
+ end
33
+ end
34
+
35
+ it 'notifies a subscribed observer after updating an item' do
36
+ @coll.add_observer( self.method(:collection_handler) )
37
+ @coll.update('abc', {a: 2}, nil)
38
+
39
+ wait 1.0 do
40
+ @action.should.be.equal :changed
41
+ end
42
+ end
43
+
44
+ it 'notifies a subscribed observer after removing an item' do
45
+ @coll.add_observer( self.method(:collection_handler) )
46
+ @coll.remove('abc')
47
+
48
+ wait 1.5 do
49
+ @action.should.be.equal :removed
50
+ end
51
+ end
52
+
53
+ it 'removes observers from the collection' do
54
+ @coll.add_observer( self.method(:collection_handler) )
55
+ @coll.observers.size.should.be.equal 1
56
+
57
+ @coll.remove_observer( self.method(:collection_handler) )
58
+ @coll.observers.size.should.be.equal 0
59
+ end
60
+
61
+ end
62
+
63
+ end