tsquery 1.0.1
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.
- checksums.yaml +7 -0
- data/lib/lazy_tsquery.rb +65 -0
- data/lib/retrying_tsquery.rb +43 -0
- data/lib/tsquery.rb +165 -0
- data/test/lazy_tsquery_test.rb +55 -0
- data/test/retrying_tsquery_test.rb +211 -0
- data/test/tsquery_test.rb +156 -0
- metadata +96 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 2020e3aad096076aabaf27fb1df6b4ca1ccceb78
|
4
|
+
data.tar.gz: 0a44ae7ec881fbf44fb79bd38d5d67d203b8150b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ea76e6eb59f873ca87115f03ae6720b6ce73e2bdf7fff5380dae0a273a6d5c470a85ef464e7483c9711cf80246326e20ab4f1bf9f93998daee79bcc5bdfff197
|
7
|
+
data.tar.gz: 0b70b805c7d44516b7f8d3960739f7aa35cf1448c5e1338b235ec37d5e10e42fd2f2cfc80877fe03ec929d814ce63cdb30f3629306234049a5d3b6f61a922635
|
data/lib/lazy_tsquery.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
require_relative './tsquery'
|
2
|
+
require 'delegate'
|
3
|
+
|
4
|
+
|
5
|
+
# LazyTsquery delays the connection and the execution of
|
6
|
+
# following commands (`use`, `login`) until another command
|
7
|
+
# is executed.
|
8
|
+
# This allows to create ready-to-use Tsquery objects without
|
9
|
+
# ever hitting the server.
|
10
|
+
class LazyTsquery < DelegateClass(Tsquery)
|
11
|
+
def connect(lazy: true, **kwargs)
|
12
|
+
if lazy
|
13
|
+
@connection_info = kwargs
|
14
|
+
nil
|
15
|
+
else
|
16
|
+
super(**kwargs)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
def execute(command, *args)
|
22
|
+
case command
|
23
|
+
when 'use', 'login'
|
24
|
+
@commands ||= []
|
25
|
+
@commands << [command, args]
|
26
|
+
|
27
|
+
nil
|
28
|
+
else
|
29
|
+
connect **@connection_info, lazy: false
|
30
|
+
|
31
|
+
@commands.each do |command, args|
|
32
|
+
super(command, *args)
|
33
|
+
end
|
34
|
+
@commands = []
|
35
|
+
|
36
|
+
super
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
# Needed for the delegation to work.
|
42
|
+
|
43
|
+
|
44
|
+
def method_missing(command, *args)
|
45
|
+
execute(command.to_s, *args)
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
def respond_to_missing?(command, *)
|
50
|
+
!!(command =~ /[[:alnum:]]$/)
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
def login(username: 'serveradmin', password:)
|
55
|
+
execute 'login', username, password
|
56
|
+
nil
|
57
|
+
rescue Error
|
58
|
+
false
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
def inspect
|
63
|
+
__getobj__.inspect
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require_relative './tsquery'
|
2
|
+
require 'delegate'
|
3
|
+
|
4
|
+
|
5
|
+
# Inspired by https://rubytapas.dpdcart.com/subscriber/post?id=689
|
6
|
+
class RetryingTsquery < DelegateClass(Tsquery)
|
7
|
+
def connect(times: 3, sleep: Kernel.method(:sleep), **keyword_args)
|
8
|
+
super(**keyword_args)
|
9
|
+
rescue Errno::ECONNREFUSED
|
10
|
+
sleep.call 0.5
|
11
|
+
retry if (times -= 1) > 0
|
12
|
+
Kernel.raise
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
def execute(*args, times: 3, sleep: Kernel.method(:sleep), **keyword_args)
|
17
|
+
super(*args, **keyword_args)
|
18
|
+
rescue Tsquery::UnknownCommand
|
19
|
+
Kernel.raise
|
20
|
+
rescue Tsquery::Error
|
21
|
+
sleep.call 0.5
|
22
|
+
retry if (times -= 1) > 0
|
23
|
+
Kernel.raise
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
# Needed for the delegation to work.
|
28
|
+
|
29
|
+
|
30
|
+
def method_missing(command, *args)
|
31
|
+
execute(command.to_s, *args)
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
def respond_to_missing?(command, *)
|
36
|
+
!!(command =~ /[[:alnum:]]$/)
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
def inspect
|
41
|
+
__getobj__.inspect
|
42
|
+
end
|
43
|
+
end
|
data/lib/tsquery.rb
ADDED
@@ -0,0 +1,165 @@
|
|
1
|
+
require 'net/telnet'
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
|
5
|
+
class Tsquery
|
6
|
+
def initialize(logger: Logger.new)
|
7
|
+
@logger = logger
|
8
|
+
end
|
9
|
+
|
10
|
+
|
11
|
+
def execute(command, *args)
|
12
|
+
args = args.each_with_object([]) do |arg, array|
|
13
|
+
case arg
|
14
|
+
when Integer
|
15
|
+
array << arg.to_s
|
16
|
+
when String
|
17
|
+
array << arg
|
18
|
+
when Hash
|
19
|
+
arg.each do |key, value|
|
20
|
+
array << "#{key}=#{value.to_s.gsub(ESCAPE_PATTERNS_REGEXP, ESCAPE_PATTERNS)}"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
full_command = ([command] + args).join(' ')
|
26
|
+
@logger.info "=> #{full_command}"
|
27
|
+
|
28
|
+
case command
|
29
|
+
when /list$/
|
30
|
+
parse_list(@telnet.cmd(
|
31
|
+
'String' => full_command,
|
32
|
+
'Timeout' => 3,
|
33
|
+
'Match' => /error id=\d+/
|
34
|
+
))
|
35
|
+
when /info$/, 'whoami', 'version'
|
36
|
+
parse_info(@telnet.cmd(
|
37
|
+
'String' => full_command,
|
38
|
+
'Timeout' => 3,
|
39
|
+
'Match' => /error id=\d+/
|
40
|
+
))
|
41
|
+
else
|
42
|
+
parse(@telnet.cmd(
|
43
|
+
'String' => full_command,
|
44
|
+
'Timeout' => 3,
|
45
|
+
'Match' => /^error id=\d+/
|
46
|
+
))
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
def login(username: 'serveradmin', password:)
|
52
|
+
execute 'login', username, password
|
53
|
+
true
|
54
|
+
rescue Error
|
55
|
+
false
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
def method_missing(command, *args)
|
60
|
+
execute(command.to_s, *args)
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
def respond_to_missing?(command, *)
|
65
|
+
!!(command =~ /[[:alnum:]]$/)
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
def connect(server: '127.0.0.1', port: '10011', telnet_class: Net::Telnet)
|
70
|
+
@telnet = telnet_class.new('Host' => server, 'Port' => port, 'Waittime' => 0.1)
|
71
|
+
@telnet.waitfor 'Match' => /^TS3\n/
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
def close
|
76
|
+
@telnet.close
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
private
|
81
|
+
# Copied from http://addons.teamspeak.com/directory/addon/integration/TeamSpeak-3-PHP-Framework.html
|
82
|
+
ESCAPE_PATTERNS = {
|
83
|
+
"/" => "\\/", # slash
|
84
|
+
" " => "\\s", # whitespace
|
85
|
+
"|" => "\\p", # pipe
|
86
|
+
";" => "\\;", # semicolon
|
87
|
+
"\a" => "\\a", # bell
|
88
|
+
"\b" => "\\b", # backspace
|
89
|
+
"\f" => "\\f", # formfeed
|
90
|
+
"\n" => "\\n", # newline
|
91
|
+
"\r" => "\\r", # carriage return
|
92
|
+
"\t" => "\\t", # horizontal tab
|
93
|
+
"\v" => "\\v", # vertical tab
|
94
|
+
"\\" => "\\\\" # backslash
|
95
|
+
}.freeze
|
96
|
+
ESCAPE_PATTERNS_REGEXP = Regexp.union(ESCAPE_PATTERNS.keys).freeze
|
97
|
+
|
98
|
+
INVERTED_ESCAPE_PATTERNS = ESCAPE_PATTERNS.invert.freeze
|
99
|
+
INVERTED_ESCAPE_PATTERNS_REGEXP = Regexp.union(INVERTED_ESCAPE_PATTERNS.keys).freeze
|
100
|
+
|
101
|
+
|
102
|
+
def parse_list(response)
|
103
|
+
check_response! response
|
104
|
+
|
105
|
+
first, last = response.split(/\n\r?/)
|
106
|
+
@logger.info "<= #{first}"
|
107
|
+
parse last
|
108
|
+
|
109
|
+
first.split('|').map do |arguments|
|
110
|
+
deserialize_arguments(arguments)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
|
115
|
+
def parse_info(response)
|
116
|
+
check_response! response
|
117
|
+
|
118
|
+
first, last = response.split(/\n\r?/)
|
119
|
+
@logger.info "<= #{first}"
|
120
|
+
parse last
|
121
|
+
|
122
|
+
deserialize_arguments(first)
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
def parse(response)
|
127
|
+
check_response! response
|
128
|
+
|
129
|
+
@logger.info "<= #{response}"
|
130
|
+
|
131
|
+
# Response always starts with "error", so we just remove it
|
132
|
+
response = response.gsub(/^error\s/, '')
|
133
|
+
arguments = deserialize_arguments(response)
|
134
|
+
|
135
|
+
raise Error, arguments['msg'] if Integer(arguments['id']) != 0
|
136
|
+
arguments['msg'] == 'ok'
|
137
|
+
end
|
138
|
+
|
139
|
+
|
140
|
+
def deserialize_arguments(string)
|
141
|
+
string.split.each_with_object({}) do |string, hash|
|
142
|
+
key, value = string.split('=')
|
143
|
+
value = value.gsub(INVERTED_ESCAPE_PATTERNS_REGEXP, INVERTED_ESCAPE_PATTERNS)
|
144
|
+
|
145
|
+
hash[key] = case value
|
146
|
+
when /^\d+$/
|
147
|
+
value.to_i
|
148
|
+
when /^\d+\.\d+$/
|
149
|
+
value.to_f
|
150
|
+
else
|
151
|
+
value
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
|
157
|
+
def check_response!(response)
|
158
|
+
raise Error, 'response is nil' if response.nil?
|
159
|
+
raise UnknownCommand, deserialize_arguments(response.gsub(/^error\s/, ''))['msg'] if response =~ /error id=256\D/
|
160
|
+
end
|
161
|
+
|
162
|
+
|
163
|
+
class Error < StandardError; end
|
164
|
+
class UnknownCommand < Error; end
|
165
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require_relative './test_helper'
|
2
|
+
|
3
|
+
|
4
|
+
class LazyTsqueryTest < Minitest::Test
|
5
|
+
def setup
|
6
|
+
@telnet = telnet = Minitest::Mock.new
|
7
|
+
@telnet_class = Class.new do
|
8
|
+
define_singleton_method :new do |*|
|
9
|
+
telnet
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
@tsquery = LazyTsquery.new(Tsquery.new(logger: Logger.new(nil)))
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
def test_it_delays_execution
|
18
|
+
@tsquery.connect telnet_class: @telnet_class
|
19
|
+
@tsquery.use 1
|
20
|
+
@tsquery.login password: 'password'
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
def test_it_executes_if_necessary
|
25
|
+
@telnet.expect :waitfor, nil, ['Match' => /^TS3\n/]
|
26
|
+
@telnet.expect :cmd, 'error id=0 msg=ok', ['String' => 'use 1', 'Timeout' => 3, 'Match' => /^error id=\d+/]
|
27
|
+
@telnet.expect :cmd, 'error id=0 msg=ok', ['String' => 'login serveradmin password', 'Timeout' => 3, 'Match' => /^error id=\d+/]
|
28
|
+
@telnet.expect :cmd, <<-RESPONSE.gsub(/^\s*/, ''), ['String' => 'version', 'Timeout' => 3, 'Match' => /error id=\d+/]
|
29
|
+
version=3.0.0-alpha4 build=9155 platform=Linux
|
30
|
+
error id=0 msg=ok
|
31
|
+
RESPONSE
|
32
|
+
|
33
|
+
@tsquery.connect telnet_class: @telnet_class
|
34
|
+
@tsquery.use 1
|
35
|
+
@tsquery.login password: 'password'
|
36
|
+
@tsquery.version
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
def test_delayed_methods_return_nil
|
41
|
+
assert_nil @tsquery.connect telnet_class: @telnet_class
|
42
|
+
assert_nil @tsquery.use 1
|
43
|
+
assert_nil @tsquery.login password: 'password'
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
def test_inspect
|
48
|
+
assert_equal @tsquery.inspect, @tsquery.__getobj__.inspect
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
def teardown
|
53
|
+
@telnet.verify
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,211 @@
|
|
1
|
+
require_relative './test_helper'
|
2
|
+
require 'delegate'
|
3
|
+
|
4
|
+
|
5
|
+
class RetryingTsqueryTest < Minitest::Test
|
6
|
+
def setup
|
7
|
+
@telnet = telnet = Minitest::Mock.new
|
8
|
+
@telnet_class = Class.new do
|
9
|
+
define_singleton_method :new do |*|
|
10
|
+
telnet
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
@tsquery = RetryingTsquery.new(Tsquery.new(logger: Logger.new(nil)))
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
def test_delegate_methods
|
19
|
+
@telnet.expect :waitfor, nil, ['Match' => /^TS3\n/]
|
20
|
+
@telnet.expect :cmd, 'error id=0 msg=ok', ['String' => 'use 1', 'Timeout' => 3, 'Match' => /^error id=\d+/]
|
21
|
+
@telnet.expect :cmd, 'error id=0 msg=ok', ['String' => 'login serveradmin password', 'Timeout' => 3, 'Match' => /^error id=\d+/]
|
22
|
+
|
23
|
+
@tsquery.connect telnet_class: @telnet_class
|
24
|
+
@tsquery.use 1
|
25
|
+
assert @tsquery.login password: 'password'
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
def test_connect
|
30
|
+
@telnet.expect :waitfor, nil, ['Match' => /^TS3\n/]
|
31
|
+
|
32
|
+
@tsquery.connect telnet_class: @telnet_class
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
def test_connect_retries_if_connection_is_refused
|
37
|
+
telnet = @telnet
|
38
|
+
times = 3
|
39
|
+
|
40
|
+
failing_telnet_class = Class.new do
|
41
|
+
define_singleton_method :new do |*|
|
42
|
+
fail Errno::ECONNREFUSED if (times -= 1) > 0
|
43
|
+
telnet
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
@telnet.expect :waitfor, nil, ['Match' => /^TS3\n/]
|
48
|
+
|
49
|
+
@tsquery.connect telnet_class: failing_telnet_class, sleep: ->(_){}
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
def test_connect_gives_up_after_3_retries
|
54
|
+
telnet = @telnet
|
55
|
+
times = 4
|
56
|
+
|
57
|
+
failing_telnet_class = Class.new do
|
58
|
+
define_singleton_method :new do |*|
|
59
|
+
fail Errno::ECONNREFUSED if (times -= 1) > 0
|
60
|
+
telnet
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
assert_raises Errno::ECONNREFUSED do
|
65
|
+
@tsquery.connect telnet_class: failing_telnet_class, sleep: ->(_){}
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
def test_execute
|
71
|
+
@telnet.expect :waitfor, nil, ['Match' => /^TS3\n/]
|
72
|
+
@telnet.expect :cmd, 'error id=0 msg=ok', ['String' => 'use 1', 'Timeout' => 3, 'Match' => /^error id=\d+/]
|
73
|
+
@telnet.expect :cmd, 'error id=0 msg=ok', ['String' => 'login serveradmin password', 'Timeout' => 3, 'Match' => /^error id=\d+/]
|
74
|
+
|
75
|
+
@tsquery.connect telnet_class: @telnet_class
|
76
|
+
@tsquery.use 1
|
77
|
+
assert @tsquery.login password: 'password'
|
78
|
+
end
|
79
|
+
|
80
|
+
|
81
|
+
def test_execute_retries_if_failing
|
82
|
+
times = 3
|
83
|
+
|
84
|
+
failing_telnet = SimpleDelegator.new(@telnet)
|
85
|
+
failing_telnet.define_singleton_method :cmd do |*args|
|
86
|
+
# cmd will fail the first x times.
|
87
|
+
return nil if (times -= 1) > 0
|
88
|
+
|
89
|
+
__getobj__.cmd *args
|
90
|
+
'error id=0 msg=ok'
|
91
|
+
end
|
92
|
+
|
93
|
+
@telnet_class = Class.new do
|
94
|
+
define_singleton_method :new do |*|
|
95
|
+
failing_telnet
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
@telnet.expect :waitfor, nil, ['Match' => /^TS3\n/]
|
100
|
+
@telnet.expect :cmd, 'error id=0 msg=ok', ['String' => 'use 1', 'Timeout' => 3, 'Match' => /^error id=\d+/]
|
101
|
+
|
102
|
+
@tsquery.connect telnet_class: @telnet_class
|
103
|
+
@tsquery.execute 'use 1', sleep: ->(*){}
|
104
|
+
end
|
105
|
+
|
106
|
+
|
107
|
+
def test_execute_gives_up_after_3_retries
|
108
|
+
times = 4
|
109
|
+
|
110
|
+
failing_telnet = SimpleDelegator.new(@telnet)
|
111
|
+
failing_telnet.define_singleton_method :cmd do |*args|
|
112
|
+
# cmd will fail the first x times.
|
113
|
+
return nil if (times -= 1) > 0
|
114
|
+
|
115
|
+
__getobj__.cmd *args
|
116
|
+
'error id=0 msg=ok'
|
117
|
+
end
|
118
|
+
|
119
|
+
@telnet_class = Class.new do
|
120
|
+
define_singleton_method :new do |*|
|
121
|
+
failing_telnet
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
@telnet.expect :waitfor, nil, ['Match' => /^TS3\n/]
|
126
|
+
|
127
|
+
@tsquery.connect telnet_class: @telnet_class
|
128
|
+
|
129
|
+
assert_raises Tsquery::Error do
|
130
|
+
@tsquery.execute 'login serveradmin password', sleep: ->(*){}
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
|
135
|
+
def test_dynamic_execute_retries_if_failing
|
136
|
+
times = 3
|
137
|
+
|
138
|
+
failing_telnet = SimpleDelegator.new(@telnet)
|
139
|
+
failing_telnet.define_singleton_method :cmd do |*args|
|
140
|
+
# cmd will fail the first x times.
|
141
|
+
return nil if (times -= 1) > 0
|
142
|
+
|
143
|
+
__getobj__.cmd *args
|
144
|
+
'error id=0 msg=ok'
|
145
|
+
end
|
146
|
+
|
147
|
+
@telnet_class = Class.new do
|
148
|
+
define_singleton_method :new do |*|
|
149
|
+
failing_telnet
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
@telnet.expect :waitfor, nil, ['Match' => /^TS3\n/]
|
154
|
+
@telnet.expect :cmd, 'error id=0 msg=ok', ['String' => 'use 1', 'Timeout' => 3, 'Match' => /^error id=\d+/]
|
155
|
+
|
156
|
+
|
157
|
+
@tsquery.connect telnet_class: @telnet_class
|
158
|
+
@tsquery.use 1, sleep: ->(*){}
|
159
|
+
end
|
160
|
+
|
161
|
+
|
162
|
+
def test_dynamic_execute_gives_up_after_3_retries
|
163
|
+
times = 4
|
164
|
+
|
165
|
+
failing_telnet = SimpleDelegator.new(@telnet)
|
166
|
+
failing_telnet.define_singleton_method :cmd do |*args|
|
167
|
+
# cmd will fail the first x times.
|
168
|
+
return nil if (times -= 1) > 0
|
169
|
+
|
170
|
+
__getobj__.cmd *args
|
171
|
+
'error id=0 msg=ok'
|
172
|
+
end
|
173
|
+
|
174
|
+
@telnet_class = Class.new do
|
175
|
+
define_singleton_method :new do |*|
|
176
|
+
failing_telnet
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
@telnet.expect :waitfor, nil, ['Match' => /^TS3\n/]
|
181
|
+
|
182
|
+
@tsquery.connect telnet_class: @telnet_class
|
183
|
+
|
184
|
+
assert_raises Tsquery::Error do
|
185
|
+
@tsquery.use 1, sleep: ->(*){}
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
|
190
|
+
def test_does_not_retry_unknown_commands
|
191
|
+
@telnet.expect :waitfor, nil, ['Match' => /^TS3\n/]
|
192
|
+
@telnet.expect :cmd, 'error id=256 msg=command\snot\sfound', ['String' => 'unknown', 'Timeout' => 3, 'Match' => /^error id=\d+/]
|
193
|
+
|
194
|
+
@tsquery.connect telnet_class: @telnet_class
|
195
|
+
|
196
|
+
ex = assert_raises Tsquery::UnknownCommand do
|
197
|
+
@tsquery.execute 'unknown', sleep: ->(*){}
|
198
|
+
end
|
199
|
+
assert_equal 'command not found', ex.message
|
200
|
+
end
|
201
|
+
|
202
|
+
|
203
|
+
def test_inspect
|
204
|
+
assert_equal @tsquery.inspect, @tsquery.__getobj__.inspect
|
205
|
+
end
|
206
|
+
|
207
|
+
|
208
|
+
def teardown
|
209
|
+
@telnet.verify
|
210
|
+
end
|
211
|
+
end
|
@@ -0,0 +1,156 @@
|
|
1
|
+
require_relative './test_helper'
|
2
|
+
|
3
|
+
|
4
|
+
class TsqueryTest < Minitest::Test
|
5
|
+
def setup
|
6
|
+
@telnet = telnet = Minitest::Mock.new
|
7
|
+
@telnet_class = Class.new do
|
8
|
+
define_singleton_method :new do |*|
|
9
|
+
telnet
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
@telnet.expect :waitfor, nil, ['Match' => /^TS3\n/]
|
14
|
+
|
15
|
+
@tsquery = Tsquery.new(logger: Logger.new(nil))
|
16
|
+
@tsquery.connect telnet_class: @telnet_class
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
def test_execute_simple_command
|
21
|
+
@telnet.expect :cmd, 'error id=0 msg=ok', ['String' => 'quit', 'Timeout' => 3, 'Match' => /^error id=\d+/]
|
22
|
+
|
23
|
+
@tsquery.execute 'quit'
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
def test_execute_unknown_command
|
28
|
+
@telnet.expect :cmd, 'error id=256 msg=command\snot\sfound', ['String' => 'unknown', 'Timeout' => 3, 'Match' => /^error id=\d+/]
|
29
|
+
|
30
|
+
assert_raises Tsquery::UnknownCommand do
|
31
|
+
@tsquery.execute 'unknown'
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
def test_execute_simple_command_with_args
|
37
|
+
@telnet.expect :cmd, 'error id=0 msg=ok', ['String' => 'login serveradmin password', 'Timeout' => 3, 'Match' => /^error id=\d+/]
|
38
|
+
|
39
|
+
@tsquery.execute 'login', 'serveradmin', 'password'
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
def test_execute_complex_command_with_args
|
44
|
+
command = 'serveredit virtualserver_name=tsjoin virtualserver_welcomemessage=Welcome\smessage'
|
45
|
+
@telnet.expect :cmd, 'error id=0 msg=ok', ['String' => command, 'Timeout' => 3, 'Match' => /^error id=\d+/]
|
46
|
+
|
47
|
+
@tsquery.execute 'serveredit',
|
48
|
+
'virtualserver_name' => 'tsjoin',
|
49
|
+
'virtualserver_welcomemessage' => 'Welcome message'
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
def test_execute_command_with_symbol_args
|
54
|
+
command = 'serveredit virtualserver_name=tsjoin virtualserver_welcomemessage=Welcome\smessage'
|
55
|
+
@telnet.expect :cmd, 'error id=0 msg=ok', ['String' => command, 'Timeout' => 3, 'Match' => /^error id=\d+/]
|
56
|
+
|
57
|
+
@tsquery.execute 'serveredit',
|
58
|
+
virtualserver_name: 'tsjoin',
|
59
|
+
virtualserver_welcomemessage: 'Welcome message'
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
def test_execute_command_with_numeric_args
|
64
|
+
@telnet.expect :cmd, 'error id=0 msg=ok', ['String' => 'serverstop sid=1', 'Timeout' => 3, 'Match' => /^error id=\d+/]
|
65
|
+
|
66
|
+
@tsquery.execute 'serverstop', sid: 1
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
def test_execute_commands_are_logged
|
71
|
+
logger = Minitest::Mock.new
|
72
|
+
logger.expect :info, nil, ['=> serverstop sid=1']
|
73
|
+
logger.expect :info, nil, ['<= error id=0 msg=ok']
|
74
|
+
|
75
|
+
@telnet.expect :waitfor, nil, ['Match' => /^TS3\n/]
|
76
|
+
@telnet.expect :cmd, 'error id=0 msg=ok', ['String' => 'serverstop sid=1', 'Timeout' => 3, 'Match' => /^error id=\d+/]
|
77
|
+
|
78
|
+
@tsquery = Tsquery.new(logger: logger)
|
79
|
+
@tsquery.connect telnet_class: @telnet_class
|
80
|
+
@tsquery.execute 'serverstop', sid: 1
|
81
|
+
logger.verify
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
def test_execute_failing_command
|
86
|
+
@telnet.expect :cmd, nil, ['String' => 'serverstop sid=1', 'Timeout' => 3, 'Match' => /^error id=\d+/]
|
87
|
+
|
88
|
+
assert_raises Tsquery::Error do
|
89
|
+
@tsquery.serverstop sid: 1
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
def test_execute_command_which_returns_properties
|
95
|
+
@telnet.expect :cmd, <<-RESPONSE.gsub(/^\s*/, ''), ['String' => 'instanceinfo', 'Timeout' => 3, 'Match' => /error id=\d+/]
|
96
|
+
serverinstance_database_version=23 serverinstance_filetransfer_port=30033
|
97
|
+
error id=0 msg=ok
|
98
|
+
RESPONSE
|
99
|
+
|
100
|
+
assert_kind_of Hash, properties = @tsquery.instanceinfo
|
101
|
+
assert_equal 23, properties.fetch('serverinstance_database_version')
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
def test_execute_command_which_returns_a_list
|
106
|
+
@telnet.expect :cmd, <<-RESPONSE.gsub(/^\s*/, ''), ['String' => 'clientlist', 'Timeout' => 3, 'Match' => /error id=\d+/]
|
107
|
+
clid=1 cid=1 client_database_id=2 client_nickname=mario client_type=0|clid=5 cid=1 client_database_id=1 client_nickname=serveradmin\\sfrom\\s127.0.0.1:10011 client_type=1
|
108
|
+
error id=0 msg=ok
|
109
|
+
RESPONSE
|
110
|
+
|
111
|
+
assert_kind_of Array, clients = @tsquery.clientlist
|
112
|
+
assert_equal 2, clients.count
|
113
|
+
assert_equal 'mario', clients.first.fetch('client_nickname')
|
114
|
+
end
|
115
|
+
|
116
|
+
|
117
|
+
def test_login
|
118
|
+
@telnet.expect :cmd, 'error id=0 msg=ok', ['String' => 'login serveradmin password', 'Timeout' => 3, 'Match' => /^error id=\d+/]
|
119
|
+
|
120
|
+
assert @tsquery.login(password: 'password')
|
121
|
+
end
|
122
|
+
|
123
|
+
|
124
|
+
def test_failing_login
|
125
|
+
@telnet.expect :cmd, 'error id=520 msg=invalid\sloginname\sor\spassword', ['String' => 'login serveradmin wrong', 'Timeout' => 3, 'Match' => /^error id=\d+/]
|
126
|
+
|
127
|
+
refute @tsquery.login(password: 'wrong')
|
128
|
+
end
|
129
|
+
|
130
|
+
|
131
|
+
def test_method_missing
|
132
|
+
@telnet.expect :cmd, 'error id=0 msg=ok', ['String' => 'logout', 'Timeout' => 3, 'Match' => /^error id=\d+/]
|
133
|
+
|
134
|
+
@tsquery.logout
|
135
|
+
end
|
136
|
+
|
137
|
+
|
138
|
+
def test_method_missing_with_arguments
|
139
|
+
command = 'serveredit virtualserver_name=tsjoin virtualserver_welcomemessage=Welcome\smessage'
|
140
|
+
@telnet.expect :cmd, 'error id=0 msg=ok', ['String' => command, 'Timeout' => 3, 'Match' => /^error id=\d+/]
|
141
|
+
|
142
|
+
@tsquery.serveredit 'virtualserver_name' => 'tsjoin', 'virtualserver_welcomemessage' => 'Welcome message'
|
143
|
+
end
|
144
|
+
|
145
|
+
|
146
|
+
def test_close
|
147
|
+
@telnet.expect :close, nil, []
|
148
|
+
|
149
|
+
@tsquery.close
|
150
|
+
end
|
151
|
+
|
152
|
+
|
153
|
+
def teardown
|
154
|
+
@telnet.verify
|
155
|
+
end
|
156
|
+
end
|
metadata
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tsquery
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mario Uher
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-08-17 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.7'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.7'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: minitest
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '5.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '5.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '10.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '10.0'
|
55
|
+
description:
|
56
|
+
email:
|
57
|
+
- uher.mario@gmail.com
|
58
|
+
executables: []
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- lib/lazy_tsquery.rb
|
63
|
+
- lib/retrying_tsquery.rb
|
64
|
+
- lib/tsquery.rb
|
65
|
+
- test/lazy_tsquery_test.rb
|
66
|
+
- test/retrying_tsquery_test.rb
|
67
|
+
- test/tsquery_test.rb
|
68
|
+
homepage: https://github.com/tsjoin/tsquery
|
69
|
+
licenses:
|
70
|
+
- MIT
|
71
|
+
metadata: {}
|
72
|
+
post_install_message:
|
73
|
+
rdoc_options: []
|
74
|
+
require_paths:
|
75
|
+
- lib
|
76
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: '0'
|
81
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
82
|
+
requirements:
|
83
|
+
- - ">="
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
requirements: []
|
87
|
+
rubyforge_project:
|
88
|
+
rubygems_version: 2.4.8
|
89
|
+
signing_key:
|
90
|
+
specification_version: 4
|
91
|
+
summary: Automate your TeamSpeak3 server with Ruby!
|
92
|
+
test_files:
|
93
|
+
- test/lazy_tsquery_test.rb
|
94
|
+
- test/retrying_tsquery_test.rb
|
95
|
+
- test/tsquery_test.rb
|
96
|
+
has_rdoc:
|