LoginSignup
0
0

More than 5 years have passed since last update.

specから理解するinfluxdb-ruby gemの機能 1

Posted at

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下のテストを扱います。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0