influxdb-rubyとは
InfluxDBの開発元のinfluxdataが出しているclient libraryです。
そのテストコードを通して、influxdbを少し理解していこうという試みです。
client_spec.rb
InfluxDBへの接続の基本的なところ
client_spec.rb
...
context "with basic auth" do
let(:args) { { auth_method: 'basic_auth' } }
let(:credentials) { "username:password" }
let(:auth_header) { { "Authorization" => "Basic " + Base64.encode64(credentials).chomp } }
let(:stub_url) { "http://influxdb.test:9999/" }
let(:url) { subject.send(:full_url, '/') }
it "GET" do
stub_request(:get, stub_url).with(headers: auth_header).to_return(body: '[]')
expect(subject.get(url, parse: true)).to eq []
end
it "POST" do
stub_request(:post, stub_url).with(headers: auth_header).to_return(status: 204)
expect(subject.post(url, {})).to be_a(Net::HTTPNoContent)
end
end
...
stubに対して、GETとPOSTをそれぞれテスト
client_spec.rb
...
describe "#full_url" do
it "returns String" do
expect(subject.send(:full_url, "/unknown")).to be_a String
end
it "escapes params" do
url = subject.send(:full_url, "/unknown", value: ' !@#$%^&*()/\\_+-=?|`~')
expect(url).to include("value=+%21%40%23%24%25%5E%26%2A%28%29%2F%5C_%2B-%3D%3F%7C%60%7E")
end
context "with prefix" do
let(:args) { { prefix: '/dev' } }
it "returns path with prefix" do
expect(subject.send(:full_url, "/series")).to start_with("/dev")
end
end
end
...
クエリ文字列を作るfull_urlの動作を確認
ちなみにfull_urlは
lib/influxdb/query/core.rbにあります。
lib/influxdb/query/core.rb
...
def full_url(path, params = {})
if config.auth_method == "params".freeze
params[:u] = config.username
params[:p] = config.password
end
query = params.map do |k, v|
[CGI.escape(k.to_s), "=".freeze, CGI.escape(v.to_s)].join
end.join("&".freeze)
URI::Generic.build(path: File.join(config.prefix, path), query: query).to_s
end
...
config_spec.rb
接続情報などを設定できるかの確認
config_spec.rb
context "with both a database and options specified" do
let(:args) do
[
"database",
host: "host",
port: "port",
username: "username",
password: "password",
time_precision: "m"
]
end
specify { expect(conf.database).to eq "database" }
specify { expect(conf.hosts).to eq ["host"] }
specify { expect(conf.port).to eq "port" }
specify { expect(conf.username).to eq "username" }
specify { expect(conf.password).to eq "password" }
specify { expect(conf.time_precision).to eq "m" }
specify { expect(conf.epoch).to be_falsey }
end
データベース名、ホスト、ユーザ名、パスワードなどの基本的な情報
logging_spec.rb
ログまわり
logging_spec.rb
...
class LoggerTest # :nodoc:
include InfluxDB::Logging
def write_to_log(level, message)
log(level, message)
end
end
...
context "when included in classes" do
subject { LoggerTest.new }
it "logs" do
expect(InfluxDB::Logging.logger).to receive(:debug).with(an_instance_of(String)).once
subject.write_to_log(:debug, 'test')
end
end
...
max_queue_spec.rb
キューの最大設定値の確認
max_queue_spec.rb
...
context "#new" do
it "allows max_depth to be set" do
expect(described_class.new(500).max).to eq 500
end
end
...
context "#push" do
let(:queue) { described_class.new(5) }
it "allows an item to be added if the queue is not full" do
expect(queue.size).to be_zero
queue.push(1)
expect(queue.size).to eq 1
end
it "doesn't allow items to be added if the queue is full" do
expect(queue.size).to be_zero
5.times { |n| queue.push(n) }
expect(queue.size).to eq 5
queue.push(6)
expect(queue.size).to eq 5
end
end
...
キューの最大値を設定でき、最大値を超えるキューは入れらないことや
入れたキューの数を確認
point_value_spec.rb
書き込もうとしているデータをdumpメソッドの出力で確認
point_value_spec.rb
...
context "with all possible data passed" do
let(:expected_value) do
'responses,region=eu,status=200 value=5i,threshold=0.54 1436349652'
end
it 'should have proper form' do
point = InfluxDB::PointValue.new(series: "responses",
values: { value: 5, threshold: 0.54 },
tags: { region: 'eu', status: 200 },
timestamp: 1_436_349_652)
expect(point.dump).to eq(expected_value)
end
end
...
context "with no tags" do
let(:expected_value) do
"responses value=5i,threshold=0.54 1436349652"
end
it 'should have proper form' do
point = InfluxDB::PointValue.new(series: "responses",
values: { value: 5, threshold: 0.54 },
timestamp: 1_436_349_652)
expect(point.dump).to eq(expected_value)
end
end
...
context "with values only" do
let(:expected_value) do
"responses value=5i,threshold=0.54"
end
it 'should have proper form' do
point = InfluxDB::PointValue.new(series: "responses",
values: { value: 5, threshold: 0.54 })
expect(point.dump).to eq(expected_value)
end
end
context "empty tag values" do
let(:expected_value) do
"responses,region=eu value=5i"
end
it "should be omitted" do
point = InfluxDB::PointValue.new(series: "responses",
values: { value: 5 },
tags: { region: "eu", status: nil, other: "", nil => "ignored", "" => "ignored" })
expect(point.dump).to eq(expected_value)
end
end
...
tagsとvaluesを設定。
timestampはoptional
query_builder_spec.rb
クエリ作成の確認
query_builder_spec.rb
...
describe "#build" do
subject { builder.build(query, params) }
context "named parameters" do
let(:query) { "SELECT value FROM rpm WHERE f = %{f_val} group by time(%{minutes}m)" }
let(:params) { { f_val: "value", minutes: 5 } }
it { is_expected.to eq "SELECT value FROM rpm WHERE f = 'value' group by time(5m)" }
context "with string keys" do
let(:params) { { "f_val" => "value", "minutes" => 5 } }
it { is_expected.to eq "SELECT value FROM rpm WHERE f = 'value' group by time(5m)" }
end
end
context "positional parameter" do
let(:query) { "SELECT value FROM rpm WHERE time > %{1}" }
let(:params) { [1_437_019_900] }
it { is_expected.to eq "SELECT value FROM rpm WHERE time > 1437019900" }
end
context "missing parameters" do
let(:query) { "SELECT value FROM rpm WHERE time > %{1}" }
let(:params) { [] }
it { expect { subject }.to raise_error(/key.1. not found/) }
end
context "extra parameters" do
let(:query) { "SELECT value FROM rpm WHERE time > %{a}" }
let(:params) { { "a" => 0, "b" => 2 } }
it { is_expected.to eq "SELECT value FROM rpm WHERE time > 0" }
end
end
...
主にSQLの生成
worker_spec.rb
worker_spec.rb
...
describe "#push" do
let(:payload1) { "responses,region=eu value=5" }
let(:payload2) { "responses,region=eu value=6" }
let(:aggregate) { "#{payload1}\n#{payload2}" }
it "writes aggregate payload to the client" do
queue = Queue.new
allow(fake_client).to receive(:write) do |data, _precision|
queue.push(data)
end
worker.push(payload1)
worker.push(payload2)
Timeout.timeout(described_class::SLEEP_INTERVAL) do
result = queue.pop
expect(result).to eq aggregate
end
end
end
...
非同期で書き込んだ2つキューの結果を
同時に受け取ることもできる確認(!?)
次回
spec/influxdb・cases下のテストを扱います。