webspicy 0.19.0 → 0.20.4

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.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/bin/webspicy +1 -2
  3. data/lib/webspicy.rb +0 -2
  4. data/lib/webspicy/configuration.rb +15 -0
  5. data/lib/webspicy/configuration/scope.rb +1 -0
  6. data/lib/webspicy/specification.rb +4 -3
  7. data/lib/webspicy/specification/condition.rb +29 -4
  8. data/lib/webspicy/specification/err.rb +18 -0
  9. data/lib/webspicy/specification/oldies.rb +4 -0
  10. data/lib/webspicy/specification/oldies/bridge.rb +32 -0
  11. data/lib/webspicy/specification/{errcondition.rb → oldies/errcondition.rb} +5 -0
  12. data/lib/webspicy/specification/{postcondition.rb → oldies/postcondition.rb} +5 -0
  13. data/lib/webspicy/specification/{precondition.rb → oldies/precondition.rb} +5 -2
  14. data/lib/webspicy/specification/post.rb +20 -0
  15. data/lib/webspicy/specification/post/missing_condition_impl.rb +15 -0
  16. data/lib/webspicy/specification/post/unexpected_condition_impl.rb +15 -0
  17. data/lib/webspicy/specification/pre.rb +19 -0
  18. data/lib/webspicy/specification/{precondition → pre}/global_request_headers.rb +4 -4
  19. data/lib/webspicy/specification/{precondition → pre}/robust_to_invalid_input.rb +4 -4
  20. data/lib/webspicy/specification/service.rb +29 -5
  21. data/lib/webspicy/specification/test_case.rb +3 -0
  22. data/lib/webspicy/support.rb +12 -2
  23. data/lib/webspicy/support/colorize.rb +6 -0
  24. data/lib/webspicy/tester.rb +89 -27
  25. data/lib/webspicy/tester/assertions.rb +2 -2
  26. data/lib/webspicy/tester/client.rb +0 -26
  27. data/lib/webspicy/tester/fakeses.rb +41 -0
  28. data/lib/webspicy/tester/fakeses/email.rb +38 -0
  29. data/lib/webspicy/tester/fakesmtp.rb +39 -0
  30. data/lib/webspicy/tester/fakesmtp/email.rb +27 -0
  31. data/lib/webspicy/tester/reporter.rb +5 -0
  32. data/lib/webspicy/tester/reporter/documentation.rb +31 -8
  33. data/lib/webspicy/tester/reporter/error_count.rb +11 -7
  34. data/lib/webspicy/tester/reporter/exceptions.rb +2 -0
  35. data/lib/webspicy/tester/reporter/file_progress.rb +5 -2
  36. data/lib/webspicy/tester/reporter/file_summary.rb +3 -2
  37. data/lib/webspicy/tester/reporter/progress.rb +6 -4
  38. data/lib/webspicy/tester/reporter/summary.rb +9 -7
  39. data/lib/webspicy/tester/result.rb +16 -13
  40. data/lib/webspicy/tester/result/errcondition_met.rb +1 -3
  41. data/lib/webspicy/tester/result/error_schema_met.rb +1 -0
  42. data/lib/webspicy/tester/result/invocation_succeeded.rb +13 -0
  43. data/lib/webspicy/tester/result/output_schema_met.rb +1 -0
  44. data/lib/webspicy/tester/result/postcondition_met.rb +1 -3
  45. data/lib/webspicy/version.rb +2 -2
  46. data/lib/webspicy/web/invocation.rb +7 -3
  47. data/spec/blackbox/commandline.yml +24 -0
  48. data/spec/blackbox/fixtures/passing/config.rb +9 -0
  49. data/spec/blackbox/fixtures/passing/formaldef/get.yml +30 -0
  50. data/spec/unit/specification/{precondition → pre}/test_global_request_headers.rb +9 -4
  51. data/spec/unit/specification/test_condition.rb +18 -0
  52. data/spec/unit/tester/fakeses/test_email.rb +40 -0
  53. data/tasks/test.rake +2 -1
  54. metadata +34 -18
@@ -66,6 +66,11 @@ module Webspicy
66
66
  }
67
67
  end
68
68
 
69
+ def find(kind)
70
+ return self if self.is_a?(kind)
71
+ raise "Missing reporter #{kind}"
72
+ end
73
+
69
74
  protected
70
75
 
71
76
  def plural(word, count)
@@ -4,61 +4,84 @@ module Webspicy
4
4
  class Documentation < Reporter
5
5
 
6
6
  module Helpers
7
+ INDENT = " ".freeze
8
+
9
+ def spec_file_line(spec_file)
10
+ relative_path = Path(spec_file).relative_to(config.folder)
11
+ colorize_section(">> #{relative_path}", config)
12
+ end
13
+
7
14
  def spec_file_error_line(spec_file, ex)
8
- str = colorize_highlight(spec_file.to_s)
9
- str += "\n " + colorize_error("X #{ex.message}")
15
+ str = ""
16
+ str += colorize_error(INDENT + "X #{ex.message}", config)
10
17
  if ex.root_cause && ex.root_cause != ex
11
- str += "\n " + colorize_error("#{ex.root_cause.message}")
18
+ str += "\n"
19
+ str += INDENT + colorize_error("#{ex.root_cause.message}", config)
12
20
  end
13
21
  str
14
22
  end
15
23
 
16
24
  def service_line(service, test_case)
17
- str = service.to_s + ", " + test_case.to_s
18
- str = colorize_highlight(str)
25
+ str = "#{service}, #{test_case}"
26
+ str = colorize_highlight(str, config)
19
27
  end
20
28
 
21
29
  def check_success_line(check)
22
- " " + colorize_success("v") + " " + check.behavior
30
+ INDENT + colorize_success("v " + check.behavior, config)
23
31
  end
24
32
 
25
33
  def check_failure_line(check, ex)
26
- " " + colorize_error("F " + ex.message)
34
+ INDENT + colorize_error("F " + ex.message, config)
27
35
  end
28
36
 
29
37
  def check_error_line(check, ex)
30
- " " + colorize_error("E " + ex.message)
38
+ INDENT + colorize_error("E " + ex.message, config)
31
39
  end
32
40
  end
33
41
  include Helpers
34
42
 
35
43
  def spec_file_error(e)
44
+ io.puts spec_file_line(spec_file)
45
+ io.puts
36
46
  io.puts spec_file_error_line(spec_file, e)
37
47
  io.puts
48
+ io.flush
49
+ end
50
+
51
+ def before_service
52
+ io.puts spec_file_line(spec_file)
53
+ io.puts
54
+ io.flush
38
55
  end
39
56
 
40
57
  def before_test_case
41
58
  io.puts service_line(service, test_case)
59
+ io.flush
42
60
  end
43
61
 
44
62
  def check_success(check)
45
63
  io.puts check_success_line(check)
64
+ io.flush
46
65
  end
47
66
 
48
67
  def check_failure(check, ex)
49
68
  io.puts check_failure_line(check, ex)
69
+ io.flush
50
70
  end
51
71
 
52
72
  def check_error(check, ex)
53
73
  io.puts check_error_line(check, ex)
74
+ io.flush
54
75
  end
55
76
 
56
77
  def test_case_done
57
78
  io.puts
79
+ io.flush
58
80
  end
59
81
 
60
82
  def service_done
61
83
  io.puts
84
+ io.flush
62
85
  end
63
86
 
64
87
  end # class Documentation
@@ -5,18 +5,22 @@ module Webspicy
5
5
 
6
6
  def initialize(*args, &bl)
7
7
  super
8
- @error_count = 0
8
+ @errors = Hash.new{|h,k| 0 }
9
9
  end
10
+ attr_reader :errors
10
11
 
11
- def on_error(*args, &bl)
12
- @error_count += 1
12
+ [
13
+ :spec_file_error,
14
+ :check_error,
15
+ :check_failure
16
+ ].each do |meth|
17
+ define_method(meth) do |*args, &bl|
18
+ @errors[meth] += 1
19
+ end
13
20
  end
14
- alias :spec_file_error :on_error
15
- alias :check_failure :on_error
16
- alias :check_error :on_error
17
21
 
18
22
  def report
19
- @error_count
23
+ @errors.values.inject(0){|memo,x| memo+x }
20
24
  end
21
25
 
22
26
  end # class ErrorCount
@@ -33,6 +33,7 @@ module Webspicy
33
33
  io.puts e
34
34
  end
35
35
  io.puts
36
+ io.flush
36
37
  end
37
38
 
38
39
  def report_failed_results
@@ -52,6 +53,7 @@ module Webspicy
52
53
  io.puts
53
54
  end
54
55
  io.puts
56
+ io.flush
55
57
  end
56
58
 
57
59
  end # class Exceptions
@@ -4,16 +4,19 @@ module Webspicy
4
4
  class FileProgress < Reporter
5
5
 
6
6
  def spec_file_error(e)
7
- io.print colorize_error("X")
7
+ io.print colorize_error("X", config)
8
+ io.flush
8
9
  end
9
10
 
10
11
  def spec_file_done
11
- io.print colorize_success(".")
12
+ io.print colorize_success(".", config)
13
+ io.flush
12
14
  end
13
15
 
14
16
  def report
15
17
  io.puts
16
18
  io.puts
19
+ io.flush
17
20
  end
18
21
 
19
22
  end # class FileProgress
@@ -22,12 +22,13 @@ module Webspicy
22
22
  msg = "#{plural('spec file', spec_files_count)}, "\
23
23
  "#{plural('error', errors_count)}"
24
24
  if success?
25
- msg = colorize_success(msg)
25
+ msg = colorize_success(msg, config)
26
26
  else
27
- msg = colorize_error(msg)
27
+ msg = colorize_error(msg, config)
28
28
  end
29
29
  io.puts(msg)
30
30
  io.puts
31
+ io.flush
31
32
  end
32
33
 
33
34
  private
@@ -4,22 +4,24 @@ module Webspicy
4
4
  class Progress < Reporter
5
5
 
6
6
  def spec_file_error(e)
7
- io.print colorize_error("X")
7
+ io.print colorize_error("X", config)
8
8
  end
9
9
 
10
10
  def after_each_done
11
11
  if result.success?
12
- io.print colorize_success(".")
12
+ io.print colorize_success(".", config)
13
13
  elsif result.failure?
14
- io.print colorize_error("F")
14
+ io.print colorize_error("F", config)
15
15
  elsif result.error?
16
- io.print colorize_error("E")
16
+ io.print colorize_error("E", config)
17
17
  end
18
+ io.flush
18
19
  end
19
20
 
20
21
  def report
21
22
  io.puts
22
23
  io.puts
24
+ io.flush
23
25
  end
24
26
 
25
27
  end # class Progress
@@ -9,18 +9,21 @@ module Webspicy
9
9
  @examples_count = 0
10
10
  @counterexamples_count = 0
11
11
  @assertions_count = 0
12
+ #
13
+ @spec_file_errors_count = 0
12
14
  @errors_count = 0
13
15
  @failures_count = 0
14
16
  end
15
17
  attr_reader :spec_files_count, :examples_count, :counterexamples_count
16
- attr_reader :assertions_count, :errors_count, :failures_count
18
+ attr_reader :assertions_count
19
+ attr_reader :spec_file_errors_count, :errors_count, :failures_count
17
20
 
18
21
  def before_spec_file
19
22
  @spec_files_count += 1
20
23
  end
21
24
 
22
25
  def spec_file_error(e)
23
- @errors_count += 1
26
+ @spec_file_errors_count += 1
24
27
  end
25
28
 
26
29
  def after_each_done
@@ -42,18 +45,17 @@ module Webspicy
42
45
  "#{plural('error', errors_count)}, "\
43
46
  "#{plural('failure', failures_count)}"
44
47
  if success?
45
- msg = colorize_success(msg)
48
+ msg = colorize_success(msg, config)
46
49
  else
47
- msg = colorize_error(msg)
50
+ msg = colorize_error(msg, config)
48
51
  end
49
52
  io.puts(msg)
50
53
  io.puts
54
+ io.flush
51
55
  end
52
56
 
53
- private
54
-
55
57
  def success?
56
- @errors_count == 0 && @failures_count == 0
58
+ @spec_file_errors_count == 0 && @errors_count == 0 && @failures_count == 0
57
59
  end
58
60
 
59
61
  end # class Summary
@@ -5,26 +5,28 @@ module Webspicy
5
5
 
6
6
  def initialize(tester)
7
7
  @tester = tester
8
+ @scope = tester.scope
9
+ @client = tester.client
10
+ @specification = tester.specification
11
+ @service = tester.service
12
+ @test_case = tester.test_case
8
13
  @invocation = tester.invocation
9
14
  @assertions = []
10
15
  @failures = []
11
16
  @errors = []
12
- check!
17
+ if @invocation
18
+ check!
19
+ else
20
+ @errors << [InvocationSuceeded.new(self), tester.invocation_error]
21
+ end
13
22
  end
14
- attr_reader :tester, :invocation
23
+ attr_reader :tester, :scope, :client
24
+ attr_reader :specification, :service, :test_case, :invocation
15
25
  attr_reader :assertions, :failures, :errors
16
26
 
17
27
  def_delegators :@tester, *[
18
- :reporter
19
- ]
20
-
21
- def_delegators :@invocation, *[
22
28
  :config,
23
- :scope,
24
- :client,
25
- :specification,
26
- :service,
27
- :test_case
29
+ :reporter
28
30
  ]
29
31
 
30
32
  def self.from(tester)
@@ -94,13 +96,13 @@ module Webspicy
94
96
 
95
97
  def check_postconditions!
96
98
  service.postconditions.each do |c|
97
- check_one! Result::PostconditionMet.new(self, c)
99
+ check_one! Result::PostconditionMet.new(self, tester.bind_condition(c))
98
100
  end
99
101
  end
100
102
 
101
103
  def check_errconditions!
102
104
  service.errconditions.each do |c|
103
- check_one! Result::ErrconditionMet.new(self, c)
105
+ check_one! Result::ErrconditionMet.new(self, tester.bind_condition(c))
104
106
  end
105
107
  end
106
108
 
@@ -130,6 +132,7 @@ module Webspicy
130
132
  end # class Tester
131
133
  end # module Webspicy
132
134
  require_relative "result/check"
135
+ require_relative "result/invocation_succeeded"
133
136
  require_relative "result/response_status_met"
134
137
  require_relative "result/response_header_met"
135
138
  require_relative "result/output_schema_met"
@@ -18,9 +18,7 @@ module Webspicy
18
18
  end
19
19
 
20
20
  def call
21
- if err = post.check(invocation)
22
- _! err
23
- end
21
+ post.check!
24
22
  end
25
23
 
26
24
  end # class ErrconditionMet
@@ -12,6 +12,7 @@ module Webspicy
12
12
  end
13
13
 
14
14
  def call
15
+ return unless invocation.is_structured_output?
15
16
  output = invocation.loaded_body
16
17
  service.error_schema.dress(output)
17
18
  rescue Finitio::TypeError => ex
@@ -0,0 +1,13 @@
1
+ module Webspicy
2
+ class Tester
3
+ class Result
4
+ class InvocationSuceeded < Check
5
+
6
+ def initialize(result)
7
+ super(result)
8
+ end
9
+
10
+ end # class InvocationSuceeded
11
+ end # class Result
12
+ end # class Tester
13
+ end # module Webspicy
@@ -12,6 +12,7 @@ module Webspicy
12
12
  end
13
13
 
14
14
  def call
15
+ return unless invocation.is_structured_output?
15
16
  output = invocation.loaded_body
16
17
  service.output_schema.dress(output)
17
18
  rescue Finitio::TypeError => ex
@@ -18,9 +18,7 @@ module Webspicy
18
18
  end
19
19
 
20
20
  def call
21
- if err = post.check(invocation)
22
- _! err
23
- end
21
+ post.check!
24
22
  end
25
23
 
26
24
  end # class PostconditionMet
@@ -1,8 +1,8 @@
1
1
  module Webspicy
2
2
  module Version
3
3
  MAJOR = 0
4
- MINOR = 19
5
- TINY = 0
4
+ MINOR = 20
5
+ TINY = 4
6
6
  end
7
7
  VERSION = "#{Version::MAJOR}.#{Version::MINOR}.#{Version::TINY}"
8
8
  end
@@ -36,11 +36,14 @@ module Webspicy
36
36
  response.body.to_s
37
37
  end
38
38
 
39
- def loaded_output
39
+ def is_structured_output?
40
40
  ct = response.content_type || test_case.expected_content_type
41
41
  ct = ct.mime_type if ct.respond_to?(:mime_type)
42
- case ct
43
- when %r{json}
42
+ ct =~ /json/
43
+ end
44
+
45
+ def loaded_output
46
+ if is_structured_output?
44
47
  raise "Body empty while expected" if raw_output.empty?
45
48
  @loaded_output ||= ::JSON.parse(response.body)
46
49
  else
@@ -50,6 +53,7 @@ module Webspicy
50
53
  alias :loaded_body :loaded_output
51
54
 
52
55
  def output
56
+ return loaded_output unless is_structured_output?
53
57
  schema = is_expected_success? ? service.output_schema : service.error_schema
54
58
  begin
55
59
  schema.dress(loaded_output)
@@ -0,0 +1,24 @@
1
+ ---
2
+ command:
3
+ webspicy {options} {args}
4
+
5
+ examples:
6
+
7
+ - description: |-
8
+ when called on a passing path
9
+ args:
10
+ - ./fixtures/passing/config.rb
11
+ assert:
12
+ exit_code:
13
+ 0
14
+ stdout: |-
15
+ GET /, when requested
16
+ v It has a 200 response status
17
+ v It has a `Content-Type: application/json` response header
18
+ v Its output meets the expected data schema
19
+ v Assert notEmpty
20
+ v Assert pathFD('', :hello => "World" )
21
+
22
+
23
+ 1 spec file, 1 example, 0 counterexample
24
+ 5 assertions, 0 error, 0 failure