turbogenerator 0.3.0 → 0.5.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.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ YjRmMzhkMTA0NGI3NTc1M2E5YTBmMGY0NzVmZjQ3MTFlY2MxNTUzYQ==
5
+ data.tar.gz: !binary |-
6
+ YzZiMDk1ODFiZWJlZTBiNTI0ZmYyYzQwMmYxYmQyYWUzOThmNjQ1Yg==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ NTlmZGVmNTNmMWM3Yjg4ZjU0NDU2ODFlMDQ4MjI5NTA5MDE5NTBiNWE1NGU0
10
+ NDk2MmZkNTRhNzFmZGQ1MTZiZDM5NDNjZjE0MjM1ODE0MTdmYWQ3MmEwMDk0
11
+ ZjA2OGNmYWE4YjBkMWY1NzI2ZjhkNjk3M2M1NzI5MjMwNzA1NWE=
12
+ data.tar.gz: !binary |-
13
+ ZDM4OTMxZDM2NzUwNmVlZjUyODQ4OTc4OTc1Y2I5ZWE0ZWJjZTA5ZjIzNWM3
14
+ ZjQwYmNlYzBhZTZlYTcwZWExZTY2ODM0ZTRkNDUwOTFhNjczZmE0MjVjMDM1
15
+ NjUxZWI1ZWRmNjI2NTJjMjhkMmU5OTM4NWFiZDJjZGRlNzYzOTM=
data/bin/turbo CHANGED
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require "thor"
4
+ require 'term/ansicolor'
4
5
  require "turbo"
5
6
 
6
7
  def get_real_path(path)
@@ -13,7 +14,7 @@ class TurboApp < Thor
13
14
  def generate(name)
14
15
 
15
16
  if File.exist? "workflows/#{name}"
16
- puts "workflow #{name} has already exist under workflows/#{name}"
17
+ puts "workflow #{name.green} has already exist under workflows/#{name}"
17
18
  return
18
19
  end
19
20
 
@@ -32,7 +33,8 @@ class TurboApp < Thor
32
33
 
33
34
  FileUtils.copy File.join(File.dirname(File.expand_path(__FILE__)), "../lib/templates/workflow.json"), "workflows/#{name}/workflow.json"
34
35
 
35
- puts "workflow #{name} is generated under workflows/#{name}"
36
+ puts "Workflow `#{name.green}` is generated under workflows/#{name}"
37
+ puts "Run `turbo start #{name.green}` to start the workflow"
36
38
  end
37
39
 
38
40
  desc "start", "specify the workflow name"
@@ -40,9 +42,24 @@ class TurboApp < Thor
40
42
  def start(workflow)
41
43
  config = options[:config]
42
44
  turbo = Turbo.new(config)
45
+
46
+ if(!File.exist?("workflows/#{workflow}/workflow.json"))
47
+ puts "Workflow `#{workflow.green}` does not exist"
48
+ puts "Run `turbo generate #{workflow.green}` to generate it first"
49
+ return
50
+ end
51
+
43
52
  turbo.run_workflow(workflow)
44
53
  end
45
54
 
55
+ desc "list", "list all workflows"
56
+ def list
57
+ if(File.exist?("workflows"))
58
+ system ('ls workflows/')
59
+ else
60
+ puts "No workflow found, you can generate one by using `turob generate my-workflow`"
61
+ end
62
+ end
46
63
  end
47
64
 
48
65
  TurboApp.start
data/lib/commander.rb ADDED
@@ -0,0 +1,93 @@
1
+ require 'erb'
2
+ require 'json'
3
+ require 'hashugar'
4
+ require 'jsonpath'
5
+
6
+ require 'term/ansicolor'
7
+
8
+ require 'rexml/document'
9
+ include REXML
10
+
11
+ require 'value_object'
12
+
13
+
14
+ class String
15
+ include Term::ANSIColor
16
+ end
17
+
18
+ def verfiy_xpath(caze, result)
19
+ xmldoc = Document.new(result)
20
+ nodes = XPath.match(xmldoc, "#{caze.success.content}")
21
+
22
+ if nodes != nil
23
+ puts "Case: ['#{caze.name}'] passed".green
24
+ else
25
+ puts "Case: ['#{caze.name}'] failed\nExpected: #{caze.success.content}\nGot: #{result}".red
26
+ end
27
+ puts "#{caze.type} #{caze.url}\n".cyan
28
+ end
29
+
30
+ def verify_regexp(caze, result)
31
+ x = result.match(/#{caze.success.content}/)
32
+
33
+ if x != nil
34
+ puts "Case: ['#{caze.name}'] passed".green
35
+ else
36
+ puts "Case: ['#{caze.name}'] failed\nExpected: #{caze.success.content}\nGot: #{result}".red
37
+ end
38
+ puts "#{caze.type} #{caze.url}\n".cyan
39
+ end
40
+
41
+ def verify_jsonpath(caze, result)
42
+ nodes = JsonPath.on(result, "#{caze.success.content}")
43
+
44
+ if nodes.size != 0
45
+ puts "Case: ['#{caze.name}'] passed".green
46
+ else
47
+ puts "Case: ['#{caze.name}'] failed\nExpected: #{nodes}\nGot: #{result}".red
48
+ end
49
+ puts "#{caze.type} #{caze.url}\n".cyan
50
+ end
51
+
52
+ def verify(caze)
53
+ result = `#{caze.command}`
54
+
55
+ case caze.success.type
56
+ when 'xpath'
57
+ verfiy_xpath(caze, result)
58
+ when 'regexp'
59
+ verify_regexp(caze, result)
60
+ when 'jsonpath'
61
+ verify_jsonpath(caze, result)
62
+ end
63
+ end
64
+
65
+ def generate_command(config)
66
+ template = File.read(File.join(File.dirname(File.expand_path(__FILE__)), 'templates/command.erb'))
67
+ renderer = ERB.new(template)
68
+
69
+ puts "Scenario: #{config.name}, test cases: #{config.cases.size}\n".cyan
70
+ config.cases.each do |caze|
71
+ test_case = TestCase.new({
72
+ :name => caze.name,
73
+ :url => "#{config.baseurl}/#{caze.path}",
74
+ :headers => caze.headers,
75
+ :type => caze.type,
76
+ :data_path => caze.data,
77
+ :success => SuccessDefinition.new(caze.success),
78
+ :debug => caze.debug
79
+ })
80
+
81
+ command = renderer.result(test_case.get_binding).gsub("\n", " ").strip
82
+
83
+ test = ExecutableTest.new({
84
+ :name => caze.name,
85
+ :type => caze.type,
86
+ :url => "#{config.baseurl}/#{caze.path}",
87
+ :command => command,
88
+ :success => SuccessDefinition.new(caze.success)
89
+ })
90
+
91
+ verify(test)
92
+ end
93
+ end
@@ -0,0 +1,18 @@
1
+ module HashRecursiveMerge
2
+ def rmerge!(other_hash)
3
+ merge!(other_hash) do |key, oldval, newval|
4
+ oldval.class == self.class ? oldval.rmerge!(newval) : newval
5
+ end
6
+ end
7
+
8
+ def rmerge(other_hash)
9
+ r = {}
10
+ merge(other_hash) do |key, oldval, newval|
11
+ r[key] = oldval.class == self.class ? oldval.rmerge(newval) : newval
12
+ end
13
+ end
14
+ end
15
+
16
+ class Hash
17
+ include HashRecursiveMerge
18
+ end
@@ -0,0 +1,18 @@
1
+ curl -s
2
+ <% if success.part == "header" %> -D - -o /dev/null<% end %>
3
+
4
+ -X <%= type %>
5
+
6
+ <% if ['POST', 'PUT'].include? type %>
7
+ --data @<%= data_path %>
8
+ <% end %>
9
+
10
+ <% headers.each_pair do |key, value| %>
11
+ --header <%= key %>=<%= value %>
12
+ <% end %>
13
+
14
+ <%= url %>
15
+
16
+ <% if debug %>
17
+ -D - -o debug.log
18
+ <% end %>
@@ -0,0 +1,8 @@
1
+ <% if nodes != nil %>
2
+ Case: ["<%= caze.name %>"] passed
3
+ <% else %>
4
+ Case: ["<%= caze.name %>"] failed
5
+ Expected: <%= caze.success.content %>
6
+ Got: <%= result %>
7
+ <% end %>
8
+ <%= caze.type %> <%= caze.url %>
@@ -1,14 +1,28 @@
1
1
  {
2
- "baseurl": "http://localhost:9527",
3
- "method": "GET",
2
+ "name": "posts",
3
+ "baseurl": "http://localhost:8080",
4
4
  "headers": {
5
5
  "Accept": "application/json",
6
6
  "Content-Type": "application/json"
7
7
  },
8
8
  "cases": [
9
- {
10
- "path": "/resource",
11
- "success": "200 OK"
9
+ { "name": "list posts",
10
+ "path": "/api/feeds",
11
+ "success": {
12
+ "part": "body",
13
+ "type": "jsonpath",
14
+ "content": "$[0].url"
15
+ }
16
+ },
17
+ { "name": "create posts",
18
+ "path": "/api/fav-feeds",
19
+ "type": "POST",
20
+ "data": "resource.json",
21
+ "success": {
22
+ "part": "header",
23
+ "type": "regexp",
24
+ "content": "200 OK"
25
+ }
12
26
  }
13
27
  ]
14
28
  }
data/lib/turbo.rb CHANGED
@@ -2,31 +2,12 @@
2
2
 
3
3
  require 'json'
4
4
  require 'term/ansicolor'
5
+ require 'hashugar'
6
+ require 'rexml/document'
7
+ include REXML
5
8
 
6
- # Author:: Simone Carletti <weppos@weppos.net>
7
- # Copyright:: 2007-2008 The Authors
8
- # License:: MIT License
9
- # Link:: http://www.simonecarletti.com/
10
- # Source:: http://gist.github.com/gists/6391/
11
- #
12
- module HashRecursiveMerge
13
- def rmerge!(other_hash)
14
- merge!(other_hash) do |key, oldval, newval|
15
- oldval.class == self.class ? oldval.rmerge!(newval) : newval
16
- end
17
- end
18
-
19
- def rmerge(other_hash)
20
- r = {}
21
- merge(other_hash) do |key, oldval, newval|
22
- r[key] = oldval.class == self.class ? oldval.rmerge(newval) : newval
23
- end
24
- end
25
- end
26
-
27
- class Hash
28
- include HashRecursiveMerge
29
- end
9
+ require 'hash_recursive'
10
+ require 'commander'
30
11
 
31
12
  class String
32
13
  include Term::ANSIColor
@@ -46,23 +27,25 @@ class Turbo
46
27
  @workflow_path = "workflows/#{workflow}"
47
28
  @pre_command = "#{@workflow_path}/#{wf['before']}"
48
29
  @post_command = "#{@workflow_path}/#{wf['after']}"
30
+ @debug_file = 'debug.log'
49
31
 
50
32
  scenarios = wf['scenarios']
33
+ @run_success = 0
34
+ @run_failed = 0
51
35
 
52
- before
36
+ execute_before_script
53
37
  scenarios.each do |scenario|
54
38
  run_scenario("#{@workflow_path}/scenarios/#{scenario}")
55
39
  end
56
- after
57
-
40
+ execute_after_script
58
41
  end
59
42
 
60
43
  private
61
- def before
44
+ def execute_before_script
62
45
  system "#{@pre_command}"
63
46
  end
64
47
 
65
- def after
48
+ def execute_after_script
66
49
  system "#{@post_command}"
67
50
  end
68
51
 
@@ -76,14 +59,6 @@ class Turbo
76
59
  end
77
60
  end
78
61
 
79
- def generate_header(obj)
80
- headers = []
81
- obj.each_pair do |k, v|
82
- headers << "-H \"#{k}: #{v}\""
83
- end
84
- headers.join(' ')
85
- end
86
-
87
62
  def load_common
88
63
  JSON.parse(File.read(@conf['conf_path'] + '/' + @conf['common_conf']))
89
64
  end
@@ -92,39 +67,18 @@ class Turbo
92
67
  JSON.parse(File.read(scenario))
93
68
  end
94
69
 
95
- def run_scenario(scenario)
96
- common = load_common
97
- config = common.rmerge(load_scenario(scenario))
70
+ def bootstrap(scenario)
71
+ config = load_common.rmerge(load_scenario(scenario)).to_hashugar
98
72
 
99
- if config['disabled']
100
- puts "skipping scenario, #{scenario}".cyan
101
- return
102
- end
103
-
104
- # generate all headers
105
- headers = generate_header(config['headers'])
106
-
107
- # generate HTTP method
108
- method = "-X #{config['method']}"
109
-
110
- # run each case here
111
- config['cases'].each do |caze|
112
- path = config['baseurl'] + caze['path']
113
- data = config['method'] == "POST" || config['method'] == "PUT" ? "-d @#{@workflow_path}/#{caze['data']}" : ""
114
-
115
- debug = @conf['debug'] == 'true' || config['debug'] == 'true' ? "-D - -o debug.log" : ""
73
+ def config.get_binding
74
+ binding
75
+ end
116
76
 
117
- real_command = "curl -is #{headers} #{method} #{data} #{path} #{debug}"
118
- puts real_command
119
- command = "#{real_command} | grep --color=auto -E \"#{caze['success']}\""
77
+ config
78
+ end
120
79
 
121
- ret = system(command)
80
+ def run_scenario(scenario)
81
+ generate_command(bootstrap(scenario))
82
+ end
122
83
 
123
- if ret
124
- puts "#{'Success'}: #{real_command}".green
125
- else
126
- puts "#{'Error'}: #{real_command}".red
127
- end
128
- end
129
- end
130
84
  end
@@ -0,0 +1,52 @@
1
+ require 'ostruct'
2
+
3
+ class TestCase
4
+ attr_accessor :name, :url, :headers, :type, :data_path, :debug, :success
5
+
6
+ def initialize(args)
7
+ obj = OpenStruct.new(args)
8
+ @name = obj.name
9
+ @url = obj.url
10
+ @headers = obj.headers || {}
11
+ @type = obj.type || "GET"
12
+ @data_path = obj.data_path
13
+ @success = obj.success
14
+ @debug = obj.debug
15
+ end
16
+
17
+ def get_binding
18
+ binding
19
+ end
20
+ end
21
+
22
+ class SuccessDefinition
23
+ attr_accessor :part, :type, :content
24
+
25
+ def initialize(args)
26
+ obj = OpenStruct.new(args)
27
+ @part = obj.part
28
+ @type = obj.type
29
+ @content = obj.content
30
+ end
31
+
32
+ def get_binding
33
+ binding
34
+ end
35
+ end
36
+
37
+ class ExecutableTest
38
+ attr_accessor :name, :type, :url, :command, :success
39
+
40
+ def initialize(args)
41
+ obj = OpenStruct.new(args)
42
+ @name = obj.name
43
+ @url = obj.url
44
+ @type = obj.type || "GET"
45
+ @command = obj.command
46
+ @success = obj.success
47
+ end
48
+
49
+ def get_binding
50
+ binding
51
+ end
52
+ end
metadata CHANGED
@@ -1,91 +1,143 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: turbogenerator
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
5
- prerelease:
4
+ version: 0.5.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Juntao Qiu
8
+ - Jia Wei
9
+ - Shen Tong
10
+ - Yan Yu
11
+ - Yang Mengmeng
9
12
  autorequire:
10
13
  bindir: bin
11
14
  cert_chain: []
12
- date: 2014-03-06 00:00:00.000000000 Z
15
+ date: 2015-03-12 00:00:00.000000000 Z
13
16
  dependencies:
14
17
  - !ruby/object:Gem::Dependency
15
18
  name: thor
16
19
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
20
  requirements:
21
+ - - ~>
22
+ - !ruby/object:Gem::Version
23
+ version: '0.18'
19
24
  - - ! '>='
20
25
  - !ruby/object:Gem::Version
21
26
  version: 0.18.1
22
27
  type: :runtime
23
28
  prerelease: false
24
29
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
30
  requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '0.18'
27
34
  - - ! '>='
28
35
  - !ruby/object:Gem::Version
29
36
  version: 0.18.1
30
37
  - !ruby/object:Gem::Dependency
31
38
  name: term-ansicolor
32
39
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
40
  requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ version: '1.3'
35
44
  - - ! '>='
36
45
  - !ruby/object:Gem::Version
37
46
  version: 1.3.0
38
47
  type: :runtime
39
48
  prerelease: false
40
49
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
50
  requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: '1.3'
43
54
  - - ! '>='
44
55
  - !ruby/object:Gem::Version
45
56
  version: 1.3.0
46
- description: turbo is a curl wrapper for make test based on HTTP (aka RESTFul App)
47
- easier
57
+ - !ruby/object:Gem::Dependency
58
+ name: hashugar
59
+ requirement: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ~>
62
+ - !ruby/object:Gem::Version
63
+ version: '1.0'
64
+ - - ! '>='
65
+ - !ruby/object:Gem::Version
66
+ version: 1.0.0
67
+ type: :runtime
68
+ prerelease: false
69
+ version_requirements: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ~>
72
+ - !ruby/object:Gem::Version
73
+ version: '1.0'
74
+ - - ! '>='
75
+ - !ruby/object:Gem::Version
76
+ version: 1.0.0
77
+ - !ruby/object:Gem::Dependency
78
+ name: jsonpath
79
+ requirement: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ~>
82
+ - !ruby/object:Gem::Version
83
+ version: '0.5'
84
+ - - ! '>='
85
+ - !ruby/object:Gem::Version
86
+ version: 0.5.7
87
+ type: :runtime
88
+ prerelease: false
89
+ version_requirements: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - ~>
92
+ - !ruby/object:Gem::Version
93
+ version: '0.5'
94
+ - - ! '>='
95
+ - !ruby/object:Gem::Version
96
+ version: 0.5.7
97
+ description: Turbo is a HTTP API tester, it's a curl wrapper
48
98
  email: juntao.qiu@gmail.com
49
99
  executables:
50
100
  - turbo
51
101
  extensions: []
52
102
  extra_rdoc_files: []
53
103
  files:
54
- - bin/before.sh,
55
104
  - bin/turbo
105
+ - lib/commander.rb
56
106
  - lib/config/browsers.json
57
107
  - lib/config/common.json
58
108
  - lib/config/turbo.conf
59
- - lib/hash_recursive_merge.rb
109
+ - lib/hash_recursive.rb
60
110
  - lib/templates/after.sh
61
111
  - lib/templates/before.sh
112
+ - lib/templates/command.erb
113
+ - lib/templates/output.erb
62
114
  - lib/templates/resource-get.json
63
115
  - lib/templates/resource.json
64
116
  - lib/templates/workflow.json
65
117
  - lib/turbo.rb
118
+ - lib/value_object.rb
66
119
  homepage: https://github.com/abruzzi/turbo
67
120
  licenses:
68
121
  - MIT
122
+ metadata: {}
69
123
  post_install_message:
70
124
  rdoc_options: []
71
125
  require_paths:
72
126
  - lib
73
127
  required_ruby_version: !ruby/object:Gem::Requirement
74
- none: false
75
128
  requirements:
76
129
  - - ! '>='
77
130
  - !ruby/object:Gem::Version
78
131
  version: '0'
79
132
  required_rubygems_version: !ruby/object:Gem::Requirement
80
- none: false
81
133
  requirements:
82
134
  - - ! '>='
83
135
  - !ruby/object:Gem::Version
84
136
  version: '0'
85
137
  requirements: []
86
138
  rubyforge_project:
87
- rubygems_version: 1.8.28
139
+ rubygems_version: 2.2.2
88
140
  signing_key:
89
- specification_version: 3
90
- summary: turbo is a curl wrapper for make test based on HTTP more easier
141
+ specification_version: 4
142
+ summary: Turbo is a HTTP API tester
91
143
  test_files: []
data/bin/before.sh, DELETED
File without changes
@@ -1,74 +0,0 @@
1
- #
2
- # = Hash Recursive Merge
3
- #
4
- # Merges a Ruby Hash recursively, Also known as deep merge.
5
- # Recursive version of Hash#merge and Hash#merge!.
6
- #
7
- # Category:: Ruby
8
- # Package:: Hash
9
- # Author:: Simone Carletti <weppos@weppos.net>
10
- # Copyright:: 2007-2008 The Authors
11
- # License:: MIT License
12
- # Link:: http://www.simonecarletti.com/
13
- # Source:: http://gist.github.com/gists/6391/
14
- #
15
- module HashRecursiveMerge
16
-
17
- #
18
- # Recursive version of Hash#merge!
19
- #
20
- # Adds the contents of +other_hash+ to +hsh+,
21
- # merging entries in +hsh+ with duplicate keys with those from +other_hash+.
22
- #
23
- # Compared with Hash#merge!, this method supports nested hashes.
24
- # When both +hsh+ and +other_hash+ contains an entry with the same key,
25
- # it merges and returns the values from both arrays.
26
- #
27
- # h1 = {"a" => 100, "b" => 200, "c" => {"c1" => 12, "c2" => 14}}
28
- # h2 = {"b" => 254, "c" => 300, "c" => {"c1" => 16, "c3" => 94}}
29
- # h1.rmerge!(h2) #=> {"a" => 100, "b" => 254, "c" => {"c1" => 16, "c2" => 14, "c3" => 94}}
30
- #
31
- # Simply using Hash#merge! would return
32
- #
33
- # h1.merge!(h2) #=> {"a" => 100, "b" = >254, "c" => {"c1" => 16, "c3" => 94}}
34
- #
35
- def rmerge!(other_hash)
36
- merge!(other_hash) do |key, oldval, newval|
37
- oldval.class == self.class ? oldval.rmerge!(newval) : newval
38
- end
39
- end
40
-
41
- #
42
- # Recursive version of Hash#merge
43
- #
44
- # Compared with Hash#merge!, this method supports nested hashes.
45
- # When both +hsh+ and +other_hash+ contains an entry with the same key,
46
- # it merges and returns the values from both arrays.
47
- #
48
- # Compared with Hash#merge, this method provides a different approch
49
- # for merging nasted hashes.
50
- # If the value of a given key is an Hash and both +other_hash+ abd +hsh
51
- # includes the same key, the value is merged instead replaced with
52
- # +other_hash+ value.
53
- #
54
- # h1 = {"a" => 100, "b" => 200, "c" => {"c1" => 12, "c2" => 14}}
55
- # h2 = {"b" => 254, "c" => 300, "c" => {"c1" => 16, "c3" => 94}}
56
- # h1.rmerge(h2) #=> {"a" => 100, "b" => 254, "c" => {"c1" => 16, "c2" => 14, "c3" => 94}}
57
- #
58
- # Simply using Hash#merge would return
59
- #
60
- # h1.merge(h2) #=> {"a" => 100, "b" = >254, "c" => {"c1" => 16, "c3" => 94}}
61
- #
62
- def rmerge(other_hash)
63
- r = {}
64
- merge(other_hash) do |key, oldval, newval|
65
- r[key] = oldval.class == self.class ? oldval.rmerge(newval) : newval
66
- end
67
- end
68
-
69
- end
70
-
71
-
72
- class Hash
73
- include HashRecursiveMerge
74
- end