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,131 @@
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/master_server'
8
+
9
+ class TestMasterServer < Test::Unit::TestCase
10
+
11
+ context 'The user' do
12
+
13
+ should 'be able to set the number of retries' do
14
+ MasterServer.retries = 5
15
+
16
+ assert_equal 5, MasterServer.send(:class_variable_get, :@@retries)
17
+ end
18
+
19
+ end
20
+
21
+ context 'A master server' do
22
+
23
+ setup do
24
+ Socket.stubs(:getaddrinfo).
25
+ with('master', 27015, Socket::AF_INET, Socket::SOCK_DGRAM).
26
+ returns [[nil, nil, 'master', '127.0.0.1']]
27
+
28
+ @server = MasterServer.new 'master', 27015
29
+ end
30
+
31
+ should 'create a client socket upon initialization' do
32
+ socket = mock
33
+ MasterServerSocket.expects(:new).with('127.0.0.1', 27015).returns socket
34
+
35
+ @server.init_socket
36
+
37
+ assert_same socket, @server.instance_variable_get(:@socket)
38
+ end
39
+
40
+ should 'be able to get a challenge' do
41
+ reply = mock :challenge => 1234
42
+
43
+ socket = @server.instance_variable_get :@socket
44
+ socket.expects(:send).with { |packet| packet.is_a? C2M_CHECKMD5_Packet }
45
+ socket.expects(:reply).returns reply
46
+
47
+ assert_equal 1234, @server.challenge
48
+ end
49
+
50
+ should 'be able to get a list of servers' do
51
+ reply1 = mock :servers => %w{127.0.0.1:27015 127.0.0.2:27015 127.0.0.3:27015}
52
+ reply2 = mock :servers => %w{127.0.0.4:27015 0.0.0.0:0}
53
+
54
+ socket = @server.instance_variable_get :@socket
55
+ socket.expects(:send).with do |packet|
56
+ packet.is_a?(A2M_GET_SERVERS_BATCH2_Packet) &&
57
+ packet.instance_variable_get(:@filter) == 'filter' &&
58
+ packet.instance_variable_get(:@region_code) == MasterServer::REGION_EUROPE &&
59
+ packet.instance_variable_get(:@start_ip) == '0.0.0.0:0'
60
+ end
61
+ socket.expects(:send).with do |packet|
62
+ packet.is_a?(A2M_GET_SERVERS_BATCH2_Packet) &&
63
+ packet.instance_variable_get(:@filter) == 'filter' &&
64
+ packet.instance_variable_get(:@region_code) == MasterServer::REGION_EUROPE &&
65
+ packet.instance_variable_get(:@start_ip) == '127.0.0.3:27015'
66
+ end
67
+ socket.expects(:reply).times(2).returns(reply1).returns reply2
68
+
69
+ servers = [['127.0.0.1', '27015'], ['127.0.0.2', '27015'], ['127.0.0.3', '27015'], ['127.0.0.4', '27015']]
70
+ assert_equal servers, @server.servers(MasterServer::REGION_EUROPE, 'filter')
71
+ end
72
+
73
+ should 'be able to send a heartbeat' do
74
+ reply1 = mock
75
+ reply2 = mock
76
+
77
+ socket = @server.instance_variable_get :@socket
78
+ socket.expects(:send).with { |packet| packet.is_a? S2M_HEARTBEAT2_Packet }
79
+ socket.expects(:reply).times(3).returns(reply1).returns(reply2).then.
80
+ raises SteamCondenser::TimeoutError
81
+
82
+ data = { :challenge => 1234 }
83
+ assert_equal [reply1, reply2], @server.send_heartbeat(data)
84
+ end
85
+
86
+ should 'not timeout if returning servers is forced' do
87
+ MasterServer.retries = 1
88
+
89
+ reply = mock :servers => %w{127.0.0.1:27015 127.0.0.2:27015 127.0.0.3:27015}
90
+
91
+ socket = @server.instance_variable_get :@socket
92
+ socket.expects(:send).with do |packet|
93
+ packet.is_a?(A2M_GET_SERVERS_BATCH2_Packet) &&
94
+ packet.instance_variable_get(:@filter) == 'filter' &&
95
+ packet.instance_variable_get(:@region_code) == MasterServer::REGION_EUROPE &&
96
+ packet.instance_variable_get(:@start_ip) == '0.0.0.0:0'
97
+ end
98
+ socket.expects(:send).with do |packet|
99
+ packet.is_a?(A2M_GET_SERVERS_BATCH2_Packet) &&
100
+ packet.instance_variable_get(:@filter) == 'filter' &&
101
+ packet.instance_variable_get(:@region_code) == MasterServer::REGION_EUROPE &&
102
+ packet.instance_variable_get(:@start_ip) == '127.0.0.3:27015'
103
+ end
104
+ socket.expects(:reply).times(2).returns(reply).then.
105
+ raises(SteamCondenser::TimeoutError)
106
+
107
+ servers = [['127.0.0.1', '27015'], ['127.0.0.2', '27015'], ['127.0.0.3', '27015']]
108
+ assert_equal servers, @server.servers(MasterServer::REGION_EUROPE, 'filter', true)
109
+ end
110
+
111
+ should 'timeout after a predefined number of retries' do
112
+ retries = rand(4) + 1
113
+ MasterServer.retries = retries
114
+
115
+ socket = @server.instance_variable_get :@socket
116
+ socket.expects(:send).times(retries).with do |packet|
117
+ packet.is_a? A2M_GET_SERVERS_BATCH2_Packet
118
+ packet.instance_variable_get(:@filter) == ''
119
+ packet.instance_variable_get(:@region_code) == MasterServer::REGION_ALL
120
+ packet.instance_variable_get(:@start_ip) == '0.0.0.0:0'
121
+ end
122
+ socket.expects(:reply).times(retries).raises SteamCondenser::TimeoutError
123
+
124
+ assert_raises SteamCondenser::TimeoutError do
125
+ @server.servers
126
+ end
127
+ end
128
+
129
+ end
130
+
131
+ end
@@ -0,0 +1,72 @@
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/server'
8
+
9
+ class TestServer < Test::Unit::TestCase
10
+
11
+ context 'A generic server' do
12
+
13
+ class GenericServer
14
+ include Server
15
+ end
16
+
17
+ should 'split IP and port combinations' do
18
+ Socket.expects(:getaddrinfo).
19
+ with('127.0.0.1', '27015', Socket::AF_INET, Socket::SOCK_DGRAM).
20
+ returns []
21
+ Socket.expects(:getaddrinfo).
22
+ with('someserver', '27015', Socket::AF_INET, Socket::SOCK_DGRAM).
23
+ returns []
24
+
25
+ GenericServer.new '127.0.0.1:27015'
26
+ GenericServer.new 'someserver:27015'
27
+ end
28
+
29
+ should 'resolve multiple DNS names' do
30
+ Socket.expects(:getaddrinfo).
31
+ with('someserver', 27015, Socket::AF_INET, Socket::SOCK_DGRAM).
32
+ returns [
33
+ [nil, nil, 'someserver', '127.0.0.1'],
34
+ [nil, nil, 'someserver2', '127.0.0.2']
35
+ ]
36
+
37
+ server = GenericServer.new 'someserver', 27015
38
+
39
+ assert_equal %w{someserver someserver2}, server.host_names
40
+ assert_equal %w{127.0.0.1 127.0.0.2}, server.ip_addresses
41
+ end
42
+
43
+ should 'rotate through multiple IP addresses' do
44
+ Socket.stubs(:getaddrinfo).returns []
45
+
46
+ server = GenericServer.new 'someserver'
47
+ server.instance_variable_set :@ip_addresses, %w{127.0.0.1 127.0.0.2}
48
+ server.instance_variable_set :@ip_address, '127.0.0.1'
49
+ server.expects(:init_socket).twice
50
+
51
+ assert_not server.rotate_ip
52
+ assert_equal '127.0.0.2', server.instance_variable_get(:@ip_address)
53
+ assert server.rotate_ip
54
+ assert_equal '127.0.0.1', server.instance_variable_get(:@ip_address)
55
+ end
56
+
57
+ should 'allow a failsafe block to execute with IP fallback' do
58
+ Socket.stubs(:getaddrinfo).returns []
59
+ block = Proc.new { raise 'error' }
60
+
61
+ server = GenericServer.new 'someserver'
62
+ server.expects(:rotate_ip).twice.returns(false).then.returns(true)
63
+
64
+ error = assert_raises RuntimeError do
65
+ server.send :failsafe, &block
66
+ end
67
+ assert_equal 'error', error.message
68
+ end
69
+
70
+ end
71
+
72
+ end
@@ -0,0 +1,140 @@
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/source_server'
8
+
9
+ class TestSourceServer < Test::Unit::TestCase
10
+
11
+ context 'The user' do
12
+
13
+ should 'be able to get a master server for Source servers' do
14
+ master = mock
15
+ MasterServer.expects(:new).with(*MasterServer::SOURCE_MASTER_SERVER).
16
+ returns master
17
+
18
+ assert_equal master, SourceServer.master
19
+ end
20
+
21
+ end
22
+
23
+ context 'A Source server' do
24
+
25
+ setup do
26
+ Socket.stubs(:getaddrinfo).
27
+ with('source', 27015, Socket::AF_INET, Socket::SOCK_DGRAM).
28
+ returns [[nil, nil, 'source', '127.0.0.1']]
29
+
30
+ @server = SourceServer.new 'source', 27015
31
+ end
32
+
33
+ should 'create client sockets upon initialization' do
34
+ socket = mock
35
+ SourceSocket.expects(:new).with('127.0.0.1', 27015).returns socket
36
+ rcon_socket = mock
37
+ RCONSocket.expects(:new).with('127.0.0.1', 27015).returns rcon_socket
38
+
39
+ @server.init_socket
40
+
41
+ assert_same socket, @server.instance_variable_get(:@socket)
42
+ assert_same rcon_socket, @server.instance_variable_get(:@rcon_socket)
43
+ end
44
+
45
+ should 'be able to authenticate successfully' do
46
+ reply = mock
47
+
48
+ rcon_socket = mock
49
+ rcon_socket.expects(:send).with do |packet|
50
+ reply.expects(:request_id).twice.returns packet.request_id
51
+
52
+ packet.is_a? RCONAuthRequest
53
+ end
54
+ rcon_socket.expects(:reply).twice.returns(nil).returns(reply)
55
+ @server.instance_variable_set :@rcon_socket, rcon_socket
56
+
57
+ assert @server.rcon_auth 'password'
58
+ assert @server.instance_variable_get(:@rcon_authenticated)
59
+ end
60
+
61
+ should 'fail to authenticate if the wrong request ID is returned' do
62
+ reply = mock
63
+ reply.expects(:request_id).twice.returns -2
64
+
65
+ rcon_socket = mock
66
+ rcon_socket.expects(:send).with { |packet| packet.is_a? RCONAuthRequest }
67
+ rcon_socket.expects(:reply).twice.returns(nil).returns(reply)
68
+ @server.instance_variable_set :@rcon_socket, rcon_socket
69
+
70
+ assert_not @server.rcon_auth 'password'
71
+ assert_not @server.instance_variable_get(:@rcon_authenticated)
72
+ end
73
+
74
+ should 'raise an error if authentication fails' do
75
+ reply = mock
76
+ reply.expects(:request_id).returns -1
77
+
78
+ rcon_socket = mock
79
+ rcon_socket.expects(:send).with { |packet| packet.is_a? RCONAuthRequest }
80
+ rcon_socket.expects(:reply).twice.returns(nil).returns(reply)
81
+ @server.instance_variable_set :@rcon_socket, rcon_socket
82
+
83
+ assert_raises RCONNoAuthError do
84
+ @server.rcon_auth 'password'
85
+ end
86
+ end
87
+
88
+ should 'raise an error if the RCON connection is not authenticated' do
89
+ assert_raises RCONNoAuthError do
90
+ @server.rcon_exec 'command'
91
+ end
92
+ end
93
+
94
+ context 'with an authenticated RCON connection' do
95
+
96
+ setup do
97
+ @rcon_socket = mock
98
+ @rcon_socket.expects(:send).with do |packet|
99
+ packet.is_a?(RCONExecRequest) &&
100
+ packet.instance_variable_get(:@content_data).string == "command\0\0" &&
101
+ packet.instance_variable_get(:@request_id) == 1234
102
+ end
103
+ @rcon_socket.expects(:send).with do |packet|
104
+ packet.is_a?(RCONTerminator) &&
105
+ packet.instance_variable_get(:@request_id) == 1234
106
+ end
107
+
108
+ @server.instance_variable_set :@rcon_authenticated, true
109
+ @server.instance_variable_set :@rcon_request_id, 1234
110
+ @server.instance_variable_set :@rcon_socket, @rcon_socket
111
+ end
112
+
113
+ should 'reset the connection if the server indicates so' do
114
+ reply = mock
115
+ reply.expects(:is_a?).with(RCONAuthResponse).returns true
116
+ @rcon_socket.expects(:reply).returns(reply)
117
+
118
+ assert_raises RCONNoAuthError do
119
+ @server.rcon_exec 'command'
120
+ end
121
+ assert_not @server.instance_variable_get(:@rcon_authenticated)
122
+ end
123
+
124
+ should 'receive the response of a command' do
125
+ reply1 = mock :response => 'test'
126
+ reply2 = mock :response => 'test'
127
+ reply3 = mock
128
+ reply3.expects(:response).twice.returns ''
129
+
130
+ @rcon_socket.expects(:reply).times(3).returns(reply1).returns(reply2).
131
+ returns reply3
132
+
133
+ assert_equal 'testtest', @server.rcon_exec('command')
134
+ end
135
+
136
+ end
137
+
138
+ end
139
+
140
+ end
@@ -0,0 +1,127 @@
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/sockets/goldsrc_socket'
8
+
9
+ class TestGoldSrcSocket < Test::Unit::TestCase
10
+
11
+ context 'A new GoldSrc socket' do
12
+
13
+ should 'know if its endpoint is a HLTV server' do
14
+ socket1 = GoldSrcSocket.new '127.0.0.1', 27015
15
+ socket2 = GoldSrcSocket.new '127.0.0.1', 27015, true
16
+
17
+ assert_not socket1.instance_variable_get(:@is_hltv)
18
+ assert socket2.instance_variable_get(:@is_hltv)
19
+ end
20
+
21
+ end
22
+
23
+ context 'A GoldSrc socket' do
24
+
25
+ setup do
26
+ @socket = GoldSrcSocket.new '127.0.0.1'
27
+ end
28
+
29
+ should 'send wrapped up RCON requests' do
30
+ packet = mock
31
+ RCONGoldSrcRequest.expects(:new).with('test').returns packet
32
+ @socket.expects(:send).with packet
33
+
34
+ @socket.rcon_send 'test'
35
+ end
36
+
37
+ should 'be able to get a RCON challenge' do
38
+ @socket.expects(:rcon_send).with 'challenge rcon'
39
+
40
+ reply = mock :response => 'hallenge rcon 12345678'
41
+ @socket.expects(:reply).returns reply
42
+
43
+ @socket.rcon_challenge
44
+
45
+ assert_equal '12345678', @socket.instance_variable_get(:@rcon_challenge)
46
+ end
47
+
48
+ should 'raise an error if the client is banned on challenge' do
49
+ @socket.expects(:rcon_send).with 'challenge rcon'
50
+
51
+ reply = mock :response => 'You have been banned from this server.'
52
+ @socket.expects(:reply).returns reply
53
+
54
+ assert_raises RCONBanError do
55
+ @socket.rcon_challenge
56
+ end
57
+ end
58
+
59
+ should 'be able to receive single packet replies' do
60
+ buffer = mock
61
+ @socket.instance_variable_set :@buffer, buffer
62
+
63
+ @socket.expects(:receive_packet).with 1400
64
+ data = mock
65
+ buffer.expects(:long).returns 0xFFFFFFFF
66
+ buffer.expects(:get).returns data
67
+ packet = mock
68
+ SteamPacketFactory.expects(:packet_from_data).with(data).returns packet
69
+
70
+ assert_equal packet, @socket.reply
71
+ end
72
+
73
+ should 'be able to receive split packet replies' do
74
+ buffer = mock
75
+ @socket.instance_variable_set :@buffer, buffer
76
+
77
+ @socket.expects(:receive_packet).with(1400)
78
+ @socket.expects(:receive_packet).with().returns 1400
79
+
80
+ data1, data2 = mock, mock
81
+ buffer.expects(:long).times(4).returns(0xFFFFFFFE).returns(1234).
82
+ returns(0xFFFFFFFE).returns 1234
83
+ buffer.expects(:byte).twice.returns(0x02).returns 0x12
84
+ buffer.expects(:get).twice.returns(data1).returns(data2)
85
+
86
+ packet = mock
87
+ SteamPacketFactory.expects(:reassemble_packet).with([data1, data2]).
88
+ returns packet
89
+
90
+ assert_equal packet, @socket.reply
91
+ end
92
+
93
+ should 'be able to send RCON commands to game servers' do
94
+ @socket.expects(:rcon_challenge).with do
95
+ @socket.instance_variable_set :@rcon_challenge, '1234'
96
+ end
97
+ @socket.expects(:rcon_send).with("rcon 1234 password command")
98
+ @socket.expects(:rcon_send).with("rcon 1234 password")
99
+ packet1 = mock :response => 'test '
100
+ packet2 = mock :response => 'test'
101
+ packet3 = mock :response => ''
102
+ @socket.expects(:reply).times(3).returns(packet1).returns(packet2).
103
+ returns packet3
104
+
105
+ assert_equal 'test test', @socket.rcon_exec('password', 'command')
106
+ end
107
+
108
+ should 'be able to send RCON commands to HLTV servers' do
109
+ @socket.instance_variable_set :@is_hltv, true
110
+
111
+ @socket.expects(:rcon_challenge).with do
112
+ @socket.instance_variable_set :@rcon_challenge, '1234'
113
+ end
114
+ @socket.expects(:rcon_send).with("rcon 1234 password command")
115
+ @socket.expects(:rcon_send).with("rcon 1234 password")
116
+ packet1 = mock :response => 'test '
117
+ packet2 = mock :response => 'test'
118
+ packet3 = mock :response => ''
119
+ @socket.expects(:reply).times(4).raises(SteamCondenser::TimeoutError).
120
+ returns(packet1).returns(packet2).returns packet3
121
+
122
+ assert_equal 'test test', @socket.rcon_exec('password', 'command')
123
+ end
124
+
125
+ end
126
+
127
+ end