From a4c345dd4f58d0fffdddf8b5f6e67eeb196d6dbc Mon Sep 17 00:00:00 2001 From: John Christopher Anderson Date: Mon, 1 Jun 2009 20:25:39 +0000 Subject: add an rspec/ruby test suite for the javascript query server git-svn-id: https://svn.apache.org/repos/asf/couchdb/trunk@780812 13f79535-47bb-0310-9956-ffa450edef68 --- test/query_server_spec.rb | 307 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 307 insertions(+) create mode 100644 test/query_server_spec.rb diff --git a/test/query_server_spec.rb b/test/query_server_spec.rb new file mode 100644 index 00000000..ac519cca --- /dev/null +++ b/test/query_server_spec.rb @@ -0,0 +1,307 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy +# of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. + + +# to run (requires ruby and rspec): +# spec test/query_server_spec.rb -f specdoc --color + +COUCH_ROOT = "#{File.dirname(__FILE__)}/.." unless defined?(COUCH_ROOT) + + +require 'open3' +require 'spec' +require 'json' + + +JSON_REQ = { + "body"=>"undefined", + "verb"=>"GET", + "info"=>{ + "disk_format_version"=>2, + "purge_seq"=>0, + "doc_count"=>9082, + "instance_start_time"=>"1243713611467271", + "update_seq"=>9512, + "disk_size"=>27541604, + "compact_running"=>false, + "db_name"=>"toast", + "doc_del_count"=>1 + }, + "cookie"=>{}, + "form"=>{}, + "query"=>{"q"=>"stuff"}, + "path"=>["toast", "_ext"], + "headers"=>{ + "User-Agent"=>"curl/7.18.1 (i386-apple-darwin9.2.2) libcurl/7.18.1 zlib/1.2.3", + "Host"=>"localhost:5984", + "Accept"=>"*/*" + } +} + +class OSProcessRunner + def self.run + trace = false + puts "launching #{run_command}" if trace + if block_given? + Open3.popen3(run_command) do |jsin, jsout, jserr| + js = QueryServerRunner.new(jsin, jsout, jserr, trace) + yield js + end + else + jsin, jsout, jserr = Open3.popen3(run_command) + QueryServerRunner.new(jsin, jsout, jserr, trace) + end + end + def initialize jsin, jsout, jserr, trace = false + @qsin = jsin + @qsout = jsout + @qserr = jserr + @trace = trace + end + def close + @qsin.close + @qsout.close + @qserr.close + end + def reset! + run(["reset"]) + end + def add_fun(fun) + run(["add_fun", fun]) + end + def get_chunk + resp = jsgets + raise "not a chunk" unless resp.first == "chunk" + return resp[1] + end + def run json + rrun json + jsgets + end + def rrun json + line = json.to_json + puts "run: #{line}" if @trace + @qsin.puts line + end + def rgets + resp = @qsout.gets + puts "got: #{resp}" if @trace + resp + end + def jsgets + resp = rgets + # err = @qserr.gets + # puts "err: #{err}" if err + if resp + rj = JSON.parse("[#{resp.chomp}]")[0] + if rj.respond_to?(:[]) && !rj.is_a?(Array) && + if rj["log"] + log = rj["log"] + puts "log: #{log}" #if @trace + rj = jsgets + end + end + rj + else + raise "no response" + end + end +end + +class QueryServerRunner < OSProcessRunner + def self.run_command + "#{COUCH_ROOT}/src/couchdb/couchjs #{COUCH_ROOT}/share/server/main.js" + end +end + +class ExternalRunner < OSProcessRunner + def self.run_command + "#{COUCH_ROOT}/src/couchdb/couchjs #{COUCH_ROOT}/share/server/echo.js" + end +end + +describe "query server normal case" do + before(:all) do + `cd #{COUCH_ROOT} && make` + @qs = QueryServerRunner.run + end + after(:all) do + @qs.close + end + it "should reset" do + @qs.run(["reset"]).should == true + end + it "should run map funs" do + @qs.reset! + @qs.run(["add_fun", %{function(doc){emit("foo",doc.a); emit("bar",doc.a)}}]).should == true + @qs.run(["add_fun", %{function(doc){emit("baz",doc.a)}}]).should == true + rows = @qs.run(["map_doc", {:a => "b"}]) + rows[0][0].should == ["foo", "b"] + rows[0][1].should == ["bar", "b"] + rows[1][0].should == ["baz", "b"] + end + describe "reduce" do + before(:all) do + @fun = <<-JS + function(keys, values, rereduce) { + return values.length; + } + JS + @qs.reset! + end + it "should reduce" do + kvs = (0...10).collect{|i|[i,i*2]} + @qs.run(["reduce", [@fun], kvs]).should == [true, [10]] + end + end + describe "rereduce" do + before(:all) do + @fun = <<-JS + function(keys, values, rereduce) { + return sum(values); + } + JS + @qs.reset! + end + it "should rereduce" do + vs = (0...10).collect{|i|i} + @qs.run(["rereduce", [@fun], vs]).should == [true, [45]] + end + end + # it "should validate" + + describe "show" do + before(:all) do + @fun = <<-JS + function(doc, req) { + return [doc.title, doc.body].join(' - ') + } + JS + @qs.reset! + end + it "should show" do + @qs.rrun(["show_doc", @fun, + {:title => "Best ever", :body => "Doc body"}, JSON_REQ]) + @qs.jsgets.should == {"body"=>"Best ever - Doc body"} + end + end + + describe "show with headers" do + before(:all) do + @fun = <<-JS + function(doc, req) { + return { + headers : {"X-Plankton":"Rusty"}, + body : [doc.title, doc.body].join(' - ') + } + + } + JS + @qs.reset! + end + it "should show" do + @qs.rrun(["show_doc", @fun, + {:title => "Best ever", :body => "Doc body"}]) + @qs.jsgets.should == {"headers"=>{"X-Plankton"=>"Rusty"}, "body"=>"Best ever - Doc body"} + end + end + + describe "list with headers" do + before(:each) do + @fun = <<-JS + function(head, row, req) { + if (head) return {headers : {"Content-Type" : "text/plain"}, code : 200, "body" : "foo"}; + if (row) return 'some "text" here'; + return "tail"; + }; + JS + @qs.reset! + @qs.add_fun(@fun).should == true + end + it "should send head, row, and tail" do + @qs.rrun(["list_begin", {"total_rows"=>1000}, {"q" => "ok"}]) + @qs.jsgets.should == {"headers"=>{"Content-Type"=>"text/plain"}, "code"=>200, "body"=>"foo"} + @qs.run(["list_row", {"foo"=>"bar"}, {"q" => "ok"}]).should == {"body"=>"some \"text\" here"} + @qs.run(["list_tail", {"foo"=>"bar"}, {"q" => "ok"}]).should == {"body"=>"tail"} + end + end + + describe "list with headers and rows" do + before(:each) do + @fun = <<-JS + function(head, row, req) { + if (head) return {headers : {"Content-Type" : "text/plain"}, code : 200, "body" : "foo"}; + if (row) return 'row value '+row.value; + return "tail "+req.q; + }; + JS + @qs.reset! + @qs.add_fun(@fun).should == true + end + it "should render rows" do + @qs.rrun(["list_begin", {"total_rows"=>1000}, {"q" => "ok"}]) + @qs.jsgets.should == {"headers"=>{"Content-Type"=>"text/plain"}, "code"=>200, "body"=>"foo"} + @qs.run(["list_row", {"value"=>"bar"}, {"q" => "ok"}]).should == {"body"=>"row value bar"} + @qs.run(["list_row", {"value"=>"baz"}, {"q" => "ok"}]).should == {"body"=>"row value baz"} + @qs.run(["list_row", {"value"=>"bam"}, {"q" => "ok"}]).should == {"body"=>"row value bam"} + @qs.run(["list_tail", {"q" => "ok"}]).should == {"body"=>"tail ok"} + end + end + end # query server normal case + + describe "query server errors" do + before(:each) do + @qs = QueryServerRunner.run + end + after(:each) do + @qs.close + end + + describe "list" do + before(:each) do + @fun = <<-JS + function(head, row, req) { + if (head) return {headers : {"Content-Type" : "text/plain"}, code : 200, "body" : "foo"}; + if (row) return 'row value '+row.value; + return "tail "+req.q; + }; + JS + @qs.run(["reset"]).should == true + @qs.add_fun(@fun).should == true + end + it "should reset in the middle" do + @qs.rrun(["list_begin", {"total_rows"=>1000}, {"q" => "ok"}]) + @qs.jsgets.should == {"headers"=>{"Content-Type"=>"text/plain"}, "code"=>200, "body"=>"foo"} + @qs.run(["list_row", {"value"=>"bar"}, {"q" => "ok"}]).should == {"body"=>"row value bar"} + @qs.run(["reset"]).should == true + end + end + end #query server that errors + +## tests for the generic "echo" external + +# describe "running an external" do +# before(:all) do +# @ext = ExternalRunner.run +# +# end +# it "should respond to 'info'" do +# @ext.rrun(['info']) +# @ext.jsgets.should == ["info", "echo", "external server that prints its arguments as JSON"] +# end +# it "should echo the request" do + +# @ext.rrun(['req', req_obj]) +# @ext.jsgets.should == ["x"] +# end +# end +# -- cgit v1.2.3