steam-condenser 1.0.2 → 1.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.
Files changed (48) hide show
  1. data/README.md +2 -1
  2. data/Rakefile +10 -1
  3. data/lib/steam-condenser/version.rb +1 -1
  4. data/lib/steam/community/app_news.rb +0 -0
  5. data/lib/steam/community/game_leaderboard.rb +199 -0
  6. data/lib/steam/community/game_leaderboard_entry.rb +43 -0
  7. data/lib/steam/community/game_stats.rb +16 -0
  8. data/lib/steam/community/steam_game.rb +18 -0
  9. data/lib/steam/community/steam_group.rb +9 -4
  10. data/lib/steam/community/steam_id.rb +45 -41
  11. data/lib/steam/community/tf2/tf2_beta_inventory.rb +22 -0
  12. data/lib/steam/community/tf2/tf2_golden_wrench.rb +0 -0
  13. data/lib/steam/community/tf2/tf2_stats.rb +10 -4
  14. data/lib/steam/community/web_api.rb +0 -0
  15. data/lib/steam/servers/master_server.rb +2 -1
  16. data/lib/steam/servers/server.rb +1 -1
  17. data/lib/steam/sockets/rcon_socket.rb +0 -1
  18. data/lib/steam/sockets/steam_socket.rb +2 -1
  19. data/steam-condenser.gemspec +5 -4
  20. data/test/core_ext/test_stringio.rb +59 -0
  21. data/test/fixtures/invalid.xml +2 -0
  22. data/test/fixtures/sonofthor.xml +882 -0
  23. data/test/fixtures/status_goldsrc +3 -0
  24. data/test/fixtures/status_source +3 -0
  25. data/test/fixtures/valve-members.xml +231 -0
  26. data/test/helper.rb +37 -0
  27. data/test/steam/communtiy/test_steam_group.rb +83 -0
  28. data/test/steam/communtiy/test_steam_id.rb +85 -0
  29. data/test/steam/communtiy/test_web_api.rb +98 -0
  30. data/test/steam/servers/test_game_server.rb +308 -0
  31. data/test/steam/servers/test_goldsrc_server.rb +59 -0
  32. data/test/steam/servers/test_master_server.rb +131 -0
  33. data/test/steam/servers/test_server.rb +72 -0
  34. data/test/steam/servers/test_source_server.rb +140 -0
  35. data/test/steam/sockets/test_goldsrc_socket.rb +127 -0
  36. data/test/steam/sockets/test_master_server_socket.rb +41 -0
  37. data/test/steam/sockets/test_rcon_socket.rb +109 -0
  38. data/test/steam/sockets/test_source_socket.rb +77 -0
  39. data/test/steam/sockets/test_steam_socket.rb +97 -0
  40. metadata +94 -34
  41. data/Gemfile.lock +0 -22
  42. data/test/query_tests.rb +0 -70
  43. data/test/rcon_tests.rb +0 -78
  44. data/test/steam/communtiy/steam_community_test_suite.rb +0 -12
  45. data/test/steam/communtiy/steam_group_tests.rb +0 -44
  46. data/test/steam/communtiy/steam_id_tests.rb +0 -49
  47. data/test/steam_community_tests.rb +0 -45
  48. data/test/stringio_additions_tests.rb +0 -77
@@ -0,0 +1,98 @@
1
+ # This code is free software; you can redistribute it and/or modify it under
2
+ # the terms of the new BSD License.
3
+ #
4
+ # Copyright (c) 2011, Sebastian Staudt
5
+
6
+ require 'helper'
7
+ require 'steam/community/web_api'
8
+
9
+ class TestWebApi < Test::Unit::TestCase
10
+
11
+ context 'The Web API' do
12
+
13
+ setup do
14
+ WebApi.api_key = '0123456789ABCDEF0123456789ABCDEF'
15
+ end
16
+
17
+ should 'allow access to the API key' do
18
+ assert_equal '0123456789ABCDEF0123456789ABCDEF', WebApi.api_key
19
+ end
20
+
21
+ should 'allow to set the API key' do
22
+ WebApi.api_key = 'FEDCBA9876543210FEDCBA9876543210'
23
+
24
+ assert_equal 'FEDCBA9876543210FEDCBA9876543210', WebApi.api_key
25
+ end
26
+
27
+ should 'fail to set an invalid API key' do
28
+ error = assert_raises WebApiError do
29
+ WebApi.api_key = 'test'
30
+ end
31
+ assert_equal 'This is not a valid Steam Web API key.', error.message
32
+ end
33
+
34
+ should 'provide easy access to JSON data' do
35
+ WebApi.expects(:get).with :json, 'interface', 'method', 2, { :test => 'param' }
36
+
37
+ WebApi.json 'interface', 'method', 2, { :test => 'param' }
38
+ end
39
+
40
+ should 'provide easy access to parsed JSON data' do
41
+ data = mock
42
+ WebApi.expects(:json).with('interface', 'method', 2, { :test => 'param' }).
43
+ returns data
44
+ MultiJson.expects(:decode).with(data, { :symbolize_keys => true }).
45
+ returns({ :result => { :status => 1 }})
46
+
47
+ assert_equal({ :status => 1 }, WebApi.json!('interface', 'method', 2, { :test => 'param' }))
48
+ end
49
+
50
+ should 'raise an error if the parsed JSON data is an error message' do
51
+ data = mock
52
+ WebApi.expects(:json).with('interface', 'method', 2, { :test => 'param' }).
53
+ returns data
54
+ MultiJson.expects(:decode).with(data, { :symbolize_keys => true }).
55
+ returns({ :result => { :status => 2, :statusDetail => 'error' } })
56
+
57
+ error = assert_raises WebApiError do
58
+ WebApi.json! 'interface', 'method', 2, { :test => 'param' }
59
+ end
60
+ assert_equal 'The Web API request failed with the following error: error (status code: 2).', error.message
61
+ end
62
+
63
+ should 'load data from the Steam Community Web API' do
64
+ data = mock :read => 'data'
65
+ WebApi.expects(:open).with do |url, options|
66
+ options == { :proxy => true } &&
67
+ url.start_with?('http://api.steampowered.com/interface/method/v0002/?') &&
68
+ (url.split('?').last.split('&') & %w{test=param format=json key=0123456789ABCDEF0123456789ABCDEF}).size == 3
69
+ end.returns data
70
+
71
+ assert_equal 'data', WebApi.get(:json, 'interface', 'method', 2, { :test => 'param' })
72
+ end
73
+
74
+ should 'handle unauthorized access error when loading data' do
75
+ io = mock :status => [401]
76
+ http_error = OpenURI::HTTPError.new '', io
77
+ WebApi.expects(:open).raises http_error
78
+
79
+ error = assert_raises WebApiError do
80
+ WebApi.get :json, 'interface', 'method', 2, { :test => 'param' }
81
+ end
82
+ assert_equal 'Your Web API request has been rejected. You most likely did not specify a valid Web API key.', error.message
83
+ end
84
+
85
+ should 'handle generic HTTP errors when loading data' do
86
+ io = mock :status => [[404, 'Not found']]
87
+ http_error = OpenURI::HTTPError.new '', io
88
+ WebApi.expects(:open).raises http_error
89
+
90
+ error = assert_raises WebApiError do
91
+ WebApi.get :json, 'interface', 'method', 2, { :test => 'param' }
92
+ end
93
+ assert_equal 'The Web API request has failed due to an HTTP error: Not found (status code: 404).', error.message
94
+ end
95
+
96
+ end
97
+
98
+ end
@@ -0,0 +1,308 @@
1
+ # This code is free software; you can redistribute it and/or modify it under
2
+ # the terms of the new BSD License.
3
+ #
4
+ # Copyright (c) 2011, Sebastian Staudt
5
+
6
+ require 'helper'
7
+ require 'steam/servers/game_server'
8
+
9
+ class TestGameServer < Test::Unit::TestCase
10
+
11
+ context 'A generic game server' do
12
+
13
+ class GenericGameServer
14
+ include GameServer
15
+ end
16
+
17
+ setup do
18
+ Socket.stubs(:getaddrinfo).
19
+ with('game', 27015, Socket::AF_INET, Socket::SOCK_DGRAM).
20
+ returns [[nil, nil, 'game', '127.0.0.1']]
21
+ @socket = mock
22
+
23
+ @server = GenericGameServer.new 'game', 27015
24
+ @server.instance_variable_set :@socket, @socket
25
+ end
26
+
27
+ should 'send packets using its client socket' do
28
+ packet = mock
29
+ @socket.expects(:send).with packet
30
+
31
+ @server.send :send_request, packet
32
+ end
33
+
34
+ should 'get replies using its client socket' do
35
+ packet = mock
36
+ @socket.expects(:reply).returns packet
37
+
38
+ assert_equal packet, @server.send(:reply)
39
+ end
40
+
41
+ should 'be able to calculate the latency of the server' do
42
+ @server.expects(:send_request).with do |packet|
43
+ packet.is_a? A2S_INFO_Packet
44
+ end
45
+ @server.expects(:reply).with { || sleep 0.05 }
46
+
47
+ @server.update_ping
48
+ assert_operator @server.instance_variable_get(:@ping), :>=, 50
49
+ end
50
+
51
+ should 'be able to get a challenge from the server' do
52
+ @server.expects(:handle_response_for_request).with :challenge
53
+
54
+ @server.update_challenge_number
55
+ end
56
+
57
+ should 'be able to get information about server' do
58
+ @server.expects(:handle_response_for_request).with :info
59
+
60
+ @server.update_server_info
61
+ end
62
+
63
+ should 'be able to get the server rules' do
64
+ @server.expects(:handle_response_for_request).with :rules
65
+
66
+ @server.update_rules
67
+ end
68
+
69
+ should 'be able to get the players on the server' do
70
+ @server.expects(:handle_response_for_request).with :players
71
+
72
+ @server.update_players
73
+ end
74
+
75
+ should 'provide a convenience wrapper for basic server methods' do
76
+ @server.expects :update_ping
77
+ @server.expects :update_server_info
78
+ @server.expects :update_challenge_number
79
+
80
+ @server.init
81
+ end
82
+
83
+ should 'know if its RCON connection is authenticated' do
84
+ assert_equal @server.instance_variable_get(:@rcon_authenticated),
85
+ @server.rcon_authenticated?
86
+ end
87
+
88
+ should 'cache the ping of the server' do
89
+ @server.expects :update_ping
90
+
91
+ @server.ping
92
+
93
+ @server.instance_variable_set :@ping, 0
94
+
95
+ @server.ping
96
+ end
97
+
98
+ should 'cache the players on the server' do
99
+ @server.expects :update_players
100
+
101
+ @server.players
102
+
103
+ @server.instance_variable_set :@player_hash, 0
104
+
105
+ @server.players
106
+ end
107
+
108
+ should 'cache the server rules' do
109
+ @server.expects :update_rules
110
+
111
+ @server.rules
112
+
113
+ @server.instance_variable_set :@rules_hash, 0
114
+
115
+ @server.rules
116
+ end
117
+
118
+ should 'cache the server information' do
119
+ @server.expects :update_server_info
120
+
121
+ @server.server_info
122
+
123
+ @server.instance_variable_set :@info_hash, 0
124
+
125
+ @server.server_info
126
+ end
127
+
128
+ context 'be able to get additional information about the players on a Source server' do
129
+
130
+ setup do
131
+ status = fixture 'status_source'
132
+
133
+ @someone = mock
134
+ @somebody = mock
135
+ player_hash = { 'someone' => @someone, 'somebody' => @somebody }
136
+ @server.instance_variable_set :@player_hash, player_hash
137
+
138
+ @server.expects(:handle_response_for_request).with :players
139
+ @server.expects(:rcon_exec).with('status').returns status
140
+
141
+ someone_data = { :name => 'someone', :userid => '1', :uniqueid => 'STEAM_0:0:123456', :score => '10', :time => '3:52', :ping => '12', :loss => '0', :state => 'active' }
142
+ somebody_data = { :name => 'somebody', :userid => '2', :uniqueid => 'STEAM_0:0:123457', :score => '3', :time => '2:42', :ping => '34', :loss => '0', :state => 'active' }
143
+
144
+ attributes = mock
145
+ GameServer.expects(:player_status_attributes).
146
+ with('userid name uniqueid score connected ping loss state').
147
+ returns attributes
148
+ GameServer.expects(:split_player_status).
149
+ with(attributes, '1 "someone" STEAM_0:0:123456 10 3:52 12 0 active').
150
+ returns somebody_data
151
+ GameServer.expects(:split_player_status).
152
+ with(attributes, '2 "somebody" STEAM_0:0:123457 3 2:42 34 0 active').
153
+ returns someone_data
154
+
155
+ @somebody.expects(:add_info).with somebody_data
156
+ @someone.expects(:add_info).with someone_data
157
+ end
158
+
159
+ should 'with the RCON password' do
160
+ @server.expects(:rcon_auth).with 'password'
161
+
162
+ @server.update_players 'password'
163
+ end
164
+
165
+ should 'if the RCON connection is authenticated' do
166
+ @server.instance_variable_set :@rcon_authenticated, true
167
+
168
+ @server.update_players
169
+ end
170
+
171
+ end
172
+
173
+ context 'be able to get additional information about the players on a GoldSrc server' do
174
+
175
+ setup do
176
+ status = fixture 'status_goldsrc'
177
+
178
+ @someone = mock
179
+ @somebody = mock
180
+ player_hash = { 'someone' => @someone, 'somebody' => @somebody }
181
+ @server.instance_variable_set :@player_hash, player_hash
182
+
183
+ @server.expects(:handle_response_for_request).with :players
184
+ @server.expects(:rcon_exec).with('status').returns status
185
+
186
+ someone_data = { :name => 'someone', :userid => '1', :uniqueid => 'STEAM_0:0:123456', :score => '10', :time => '3:52', :ping => '12', :loss => '0', :adr => '0' }
187
+ somebody_data = { :name => 'somebody', :userid => '2', :uniqueid => 'STEAM_0:0:123457', :score => '3', :time => '2:42', :ping => '34', :loss => '0', :adr => '0' }
188
+
189
+ attributes = mock
190
+ GameServer.expects(:player_status_attributes).
191
+ with('name userid uniqueid frag time ping loss adr').
192
+ returns attributes
193
+ GameServer.expects(:split_player_status).
194
+ with(attributes, '1 "someone" 1 STEAM_0:0:123456 10 3:52 12 0 0').
195
+ returns somebody_data
196
+ GameServer.expects(:split_player_status).
197
+ with(attributes, '2 "somebody" 2 STEAM_0:0:123457 3 2:42 34 0 0').
198
+ returns someone_data
199
+
200
+ @somebody.expects(:add_info).with somebody_data
201
+ @someone.expects(:add_info).with someone_data
202
+ end
203
+
204
+ should 'with the RCON password' do
205
+ @server.expects(:rcon_auth).with 'password'
206
+
207
+ @server.update_players 'password'
208
+ end
209
+
210
+ should 'if the RCON connection is authenticated' do
211
+ @server.instance_variable_set :@rcon_authenticated, true
212
+
213
+ @server.update_players
214
+ end
215
+
216
+ end
217
+
218
+ should 'handle challenge requests' do
219
+ @server.expects(:send_request).with do |packet|
220
+ packet.is_a? A2S_SERVERQUERY_GETCHALLENGE_Packet
221
+ end
222
+
223
+ packet = mock
224
+ packet.expects(:kind_of?).with(S2A_INFO_BasePacket).returns false
225
+ packet.expects(:kind_of?).with(S2A_PLAYER_Packet).returns false
226
+ packet.expects(:kind_of?).with(S2A_RULES_Packet).returns false
227
+ packet.expects(:kind_of?).with(S2C_CHALLENGE_Packet).twice.returns true
228
+
229
+ packet.expects(:challenge_number).returns 1234
230
+ @server.expects(:reply).returns packet
231
+
232
+ @server.handle_response_for_request :challenge
233
+
234
+ assert_equal 1234, @server.instance_variable_get(:@challenge_number)
235
+ end
236
+
237
+ should 'handle info requests' do
238
+ @server.expects(:send_request).with do |packet|
239
+ packet.is_a? A2S_INFO_Packet
240
+ end
241
+
242
+ packet = mock
243
+ packet.expects(:kind_of?).with(S2A_INFO_BasePacket).twice.returns true
244
+ packet.expects(:info_hash).returns({ :test => 'test' })
245
+ @server.expects(:reply).returns packet
246
+
247
+ @server.handle_response_for_request :info
248
+
249
+ assert_equal({ :test => 'test' }, @server.instance_variable_get(:@info_hash))
250
+ end
251
+
252
+ should 'server rule requests' do
253
+ @server.expects(:send_request).with do |packet|
254
+ packet.is_a? A2S_RULES_Packet
255
+ end
256
+
257
+ packet = mock
258
+ packet.expects(:kind_of?).with(S2A_INFO_BasePacket).returns false
259
+ packet.expects(:kind_of?).with(S2A_PLAYER_Packet).returns false
260
+ packet.expects(:kind_of?).with(S2A_RULES_Packet).twice.returns true
261
+ packet.expects(:rules_hash).returns({ :test => 'test' })
262
+ @server.expects(:reply).returns packet
263
+
264
+ @server.handle_response_for_request :rules
265
+
266
+ assert_equal({ :test => 'test' }, @server.instance_variable_get(:@rules_hash))
267
+ end
268
+
269
+ should 'player requests' do
270
+ @server.expects(:send_request).with do |packet|
271
+ packet.is_a? A2S_PLAYER_Packet
272
+ end
273
+
274
+ packet = mock
275
+ packet.expects(:kind_of?).with(S2A_INFO_BasePacket).returns false
276
+ packet.expects(:kind_of?).with(S2A_PLAYER_Packet).twice.returns true
277
+ packet.expects(:player_hash).returns({ :test => 'test' })
278
+ @server.expects(:reply).returns packet
279
+
280
+ @server.handle_response_for_request :players
281
+
282
+ assert_equal({ :test => 'test' }, @server.instance_variable_get(:@player_hash))
283
+ end
284
+
285
+ should 'handle unexpected answers and retry' do
286
+ @server.expects(:send_request).twice.with do |packet|
287
+ packet.is_a? A2S_PLAYER_Packet
288
+ end
289
+
290
+ packet1 = mock
291
+ packet1.expects(:kind_of?).with(S2A_INFO_BasePacket).returns true
292
+ packet1.expects(:kind_of?).with(S2A_PLAYER_Packet).returns false
293
+ packet1.expects(:info_hash).returns({ :test => 'test1' })
294
+ packet2 = mock
295
+ packet2.expects(:kind_of?).with(S2A_INFO_BasePacket).returns false
296
+ packet2.expects(:kind_of?).with(S2A_PLAYER_Packet).twice.returns true
297
+ packet2.expects(:player_hash).returns({ :test => 'test2' })
298
+ @server.expects(:reply).twice.returns(packet1).returns packet2
299
+
300
+ @server.handle_response_for_request :players
301
+
302
+ assert_equal({ :test => 'test1' }, @server.instance_variable_get(:@info_hash))
303
+ assert_equal({ :test => 'test2' }, @server.instance_variable_get(:@player_hash))
304
+ end
305
+
306
+ end
307
+
308
+ end
@@ -0,0 +1,59 @@
1
+ # This code is free software; you can redistribute it and/or modify it under
2
+ # the terms of the new BSD License.
3
+ #
4
+ # Copyright (c) 2011, Sebastian Staudt
5
+
6
+ require 'helper'
7
+ require 'steam/servers/goldsrc_server'
8
+
9
+ class TestGoldSrcServer < Test::Unit::TestCase
10
+
11
+ context 'The user' do
12
+
13
+ should 'be able to get a master server for GoldSrc servers' do
14
+ master = mock
15
+ MasterServer.expects(:new).with(*MasterServer::GOLDSRC_MASTER_SERVER).
16
+ returns master
17
+
18
+ assert_equal master, GoldSrcServer.master
19
+ end
20
+
21
+ end
22
+
23
+ context 'A GoldSrc server' do
24
+
25
+ setup do
26
+ Socket.stubs(:getaddrinfo).
27
+ with('goldsrc', 27015, Socket::AF_INET, Socket::SOCK_DGRAM).
28
+ returns [[nil, nil, 'goldsrc', '127.0.0.1']]
29
+
30
+ @server = GoldSrcServer.new 'goldsrc', 27015
31
+ end
32
+
33
+ should 'create a client socket upon initialization' do
34
+ socket = mock
35
+ GoldSrcSocket.expects(:new).with('127.0.0.1', 27015, false).returns socket
36
+
37
+ @server.init_socket
38
+
39
+ assert_same socket, @server.instance_variable_get(:@socket)
40
+ end
41
+
42
+ should 'save the RCON password for further use' do
43
+ assert @server.rcon_auth 'password'
44
+ assert_equal 'password', @server.instance_variable_get(:@rcon_password)
45
+ end
46
+
47
+ should 'send RCON commands with the password' do
48
+ socket = mock
49
+ socket.expects(:rcon_exec).with('password', 'command').returns 'test '
50
+
51
+ @server.instance_variable_set :@rcon_password, 'password'
52
+ @server.instance_variable_set :@socket, socket
53
+
54
+ assert_equal 'test', @server.rcon_exec('command')
55
+ end
56
+
57
+ end
58
+
59
+ end