constable-rails 0.1.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 +7 -0
- data/CHANGELOG.md +88 -0
- data/LICENSE.txt +21 -0
- data/README.md +515 -0
- data/exe/constable +7 -0
- data/lib/constable/case.rb +336 -0
- data/lib/constable/cli.rb +475 -0
- data/lib/constable/cold_case/minitest.rb +342 -0
- data/lib/constable/cold_case/rspec.rb +334 -0
- data/lib/constable/cold_case.rb +280 -0
- data/lib/constable/config.rb +125 -0
- data/lib/constable/coverage.rb +951 -0
- data/lib/constable/diff.rb +212 -0
- data/lib/constable/dsl.rb +833 -0
- data/lib/constable/identity.rb +121 -0
- data/lib/constable/importer/modernizer.rb +860 -0
- data/lib/constable/importer/reopener.rb +468 -0
- data/lib/constable/importer.rb +51 -0
- data/lib/constable/investigation.rb +67 -0
- data/lib/constable/isolation.rb +171 -0
- data/lib/constable/jail.rb +399 -0
- data/lib/constable/log_router.rb +197 -0
- data/lib/constable/matchers.rb +834 -0
- data/lib/constable/order_audit.rb +130 -0
- data/lib/constable/rails_support.rb +213 -0
- data/lib/constable/railtie.rb +36 -0
- data/lib/constable/registry.rb +57 -0
- data/lib/constable/reporter.rb +625 -0
- data/lib/constable/result.rb +149 -0
- data/lib/constable/runner.rb +697 -0
- data/lib/constable/selection.rb +205 -0
- data/lib/constable/storage/adapter.rb +91 -0
- data/lib/constable/storage/mysql_adapter.rb +125 -0
- data/lib/constable/storage/postgres_adapter.rb +125 -0
- data/lib/constable/storage/sqlite_adapter.rb +84 -0
- data/lib/constable/storage.rb +847 -0
- data/lib/constable/version.rb +5 -0
- data/lib/constable/warrants.rb +290 -0
- data/lib/constable-rails.rb +16 -0
- data/lib/constable.rb +151 -0
- data/lib/generators/constable/base.rb +99 -0
- data/lib/generators/constable/channel/channel_generator.rb +20 -0
- data/lib/generators/constable/channel/templates/channel_case.rb.tt +29 -0
- data/lib/generators/constable/controller/controller_generator.rb +25 -0
- data/lib/generators/constable/controller/templates/controller_case.rb.tt +32 -0
- data/lib/generators/constable/generator/generator_generator.rb +31 -0
- data/lib/generators/constable/generator/templates/generator_case.rb.tt +28 -0
- data/lib/generators/constable/helper/helper_generator.rb +23 -0
- data/lib/generators/constable/helper/templates/helper_case.rb.tt +19 -0
- data/lib/generators/constable/import_generator.rb +137 -0
- data/lib/generators/constable/install_generator.rb +188 -0
- data/lib/generators/constable/integration/integration_generator.rb +27 -0
- data/lib/generators/constable/integration/templates/request_case.rb.tt +22 -0
- data/lib/generators/constable/job/job_generator.rb +20 -0
- data/lib/generators/constable/job/templates/job_case.rb.tt +33 -0
- data/lib/generators/constable/mailbox/mailbox_generator.rb +20 -0
- data/lib/generators/constable/mailbox/templates/mailbox_case.rb.tt +26 -0
- data/lib/generators/constable/mailer/mailer_generator.rb +32 -0
- data/lib/generators/constable/mailer/templates/mailer_case.rb.tt +34 -0
- data/lib/generators/constable/mailer/templates/preview.rb.tt +14 -0
- data/lib/generators/constable/model/model_generator.rb +31 -0
- data/lib/generators/constable/model/templates/model_case.rb.tt +37 -0
- data/lib/generators/constable/resource/resource_generator.rb +27 -0
- data/lib/generators/constable/scaffold/scaffold_generator.rb +42 -0
- data/lib/generators/constable/scaffold/templates/api_controller_case.rb.tt +54 -0
- data/lib/generators/constable/scaffold/templates/controller_case.rb.tt +70 -0
- data/lib/generators/constable/scaffold/templates/system_case.rb.tt +53 -0
- data/lib/generators/constable/system/system_generator.rb +20 -0
- data/lib/generators/constable/system/templates/system_case.rb.tt +18 -0
- data/lib/generators/constable/templates/authenticatable.rb.tt +31 -0
- data/lib/generators/constable/templates/case_helper.rb.tt +179 -0
- data/lib/generators/constable/templates/config.yml.tt +67 -0
- data/lib/generators/constable/templates/example_case.rb.tt +56 -0
- data/lib/generators/constable/templates/matchers.rb.tt +36 -0
- data/lib/generators/constable/templates/rubocop.yml.tt +12 -0
- metadata +209 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Constable
|
|
4
|
+
# The outcome of a single test, native or cold. Everything downstream -- the reporter,
|
|
5
|
+
# flake history, jail, warrants -- consumes these and nothing else.
|
|
6
|
+
class Result
|
|
7
|
+
STATUSES = %i[passed failed jailed skipped errored warranted parole_violation].freeze
|
|
8
|
+
|
|
9
|
+
GLYPHS = {
|
|
10
|
+
passed: "✓", # check
|
|
11
|
+
failed: "✗", # ballot x
|
|
12
|
+
jailed: "⛓", # chains
|
|
13
|
+
skipped: "○", # hollow circle
|
|
14
|
+
errored: "!",
|
|
15
|
+
warranted: "⚖", # scales
|
|
16
|
+
parole_violation: "⛓"
|
|
17
|
+
}.freeze
|
|
18
|
+
|
|
19
|
+
attr_reader :identity, :case_name, :description, :file, :line, :kind, :tier
|
|
20
|
+
attr_accessor :status, :duration, :failure, :warnings, :retries, :jail_reason,
|
|
21
|
+
:parole_day, :times_jailed, :seed, :coverage
|
|
22
|
+
|
|
23
|
+
def initialize(identity:, case_name:, description:, file:, line:, kind: :native, tier: nil,
|
|
24
|
+
status: :passed, duration: 0.0, failure: nil, warnings: [], retries: [])
|
|
25
|
+
@identity = identity
|
|
26
|
+
@case_name = case_name
|
|
27
|
+
@description = description
|
|
28
|
+
@file = file
|
|
29
|
+
@line = line
|
|
30
|
+
@kind = kind
|
|
31
|
+
@tier = tier
|
|
32
|
+
@status = status
|
|
33
|
+
@duration = duration
|
|
34
|
+
@failure = failure
|
|
35
|
+
@warnings = warnings
|
|
36
|
+
@retries = retries
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def self.from_investigation(investigation, **attrs)
|
|
40
|
+
new(
|
|
41
|
+
identity: investigation.identity,
|
|
42
|
+
case_name: investigation.case_name,
|
|
43
|
+
description: investigation.full_description,
|
|
44
|
+
file: investigation.relative_file,
|
|
45
|
+
line: investigation.line,
|
|
46
|
+
kind: investigation.kind,
|
|
47
|
+
tier: investigation.tier,
|
|
48
|
+
**attrs
|
|
49
|
+
)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def passed? = @status == :passed
|
|
53
|
+
def failed? = %i[failed errored].include?(@status)
|
|
54
|
+
def jailed? = %i[jailed parole_violation].include?(@status)
|
|
55
|
+
def skipped? = @status == :skipped
|
|
56
|
+
def native? = @kind == :native
|
|
57
|
+
def cold? = @kind == :cold
|
|
58
|
+
def parole_violation? = @status == :parole_violation
|
|
59
|
+
def warranted? = @status == :warranted
|
|
60
|
+
|
|
61
|
+
def glyph = GLYPHS.fetch(@status, "?")
|
|
62
|
+
def location = "#{@file}:#{@line}"
|
|
63
|
+
def display_label = "#{@case_name} \"#{@description}\""
|
|
64
|
+
|
|
65
|
+
# The command that reruns exactly this test, seed included, ready to paste.
|
|
66
|
+
def rerun_command
|
|
67
|
+
base = "constable test #{location}"
|
|
68
|
+
base += " --unsafe" if cold?
|
|
69
|
+
base += " --seed #{@seed}" if @seed
|
|
70
|
+
base
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def to_h
|
|
74
|
+
{
|
|
75
|
+
identity: @identity, case_name: @case_name, description: @description,
|
|
76
|
+
file: @file, line: @line, kind: @kind, tier: @tier, status: @status,
|
|
77
|
+
duration: @duration, failure: @failure&.to_h, warnings: @warnings,
|
|
78
|
+
retries: @retries, jail_reason: @jail_reason, parole_day: @parole_day,
|
|
79
|
+
times_jailed: @times_jailed, seed: @seed
|
|
80
|
+
}
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def self.from_h(hash)
|
|
84
|
+
hash = hash.transform_keys(&:to_sym)
|
|
85
|
+
result = new(
|
|
86
|
+
identity: hash[:identity], case_name: hash[:case_name], description: hash[:description],
|
|
87
|
+
file: hash[:file], line: hash[:line], kind: (hash[:kind] || :native).to_sym,
|
|
88
|
+
tier: hash[:tier], status: (hash[:status] || :passed).to_sym,
|
|
89
|
+
duration: hash[:duration].to_f, warnings: hash[:warnings] || [], retries: hash[:retries] || []
|
|
90
|
+
)
|
|
91
|
+
result.failure = Failure.from_h(hash[:failure]) if hash[:failure]
|
|
92
|
+
result.jail_reason = hash[:jail_reason]
|
|
93
|
+
result.parole_day = hash[:parole_day]
|
|
94
|
+
result.times_jailed = hash[:times_jailed]
|
|
95
|
+
result.seed = hash[:seed]
|
|
96
|
+
result
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# A failure carries its own context -- the assertion's actual message plus whatever
|
|
101
|
+
# the matcher thought was worth showing (a response body, a record's attributes) --
|
|
102
|
+
# and points at the investigate block, never at framework internals.
|
|
103
|
+
class Failure
|
|
104
|
+
attr_reader :message, :context, :backtrace, :exception_class
|
|
105
|
+
|
|
106
|
+
def initialize(message:, context: nil, backtrace: [], exception_class: nil)
|
|
107
|
+
# Other engines wrap their messages in blank lines for their own reporters. Ours
|
|
108
|
+
# already puts the message in a block of its own, so the padding just leaves holes.
|
|
109
|
+
@message = message.is_a?(String) ? message.strip : message
|
|
110
|
+
@context = context
|
|
111
|
+
@backtrace = Array(backtrace)
|
|
112
|
+
@exception_class = exception_class
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def self.from_exception(error, context: nil)
|
|
116
|
+
new(
|
|
117
|
+
message: error.message,
|
|
118
|
+
context: context,
|
|
119
|
+
backtrace: Backtrace.clean(error.backtrace),
|
|
120
|
+
exception_class: error.class.name
|
|
121
|
+
)
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def to_h
|
|
125
|
+
{ message: @message, context: @context, backtrace: @backtrace, exception_class: @exception_class }
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def self.from_h(hash)
|
|
129
|
+
return nil if hash.nil?
|
|
130
|
+
|
|
131
|
+
hash = hash.transform_keys(&:to_sym)
|
|
132
|
+
new(message: hash[:message], context: hash[:context],
|
|
133
|
+
backtrace: hash[:backtrace] || [], exception_class: hash[:exception_class])
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Strips the framework out of backtraces. A developer wants their own line, not ours.
|
|
138
|
+
module Backtrace
|
|
139
|
+
GEM_ROOT = File.expand_path("../..", __dir__)
|
|
140
|
+
|
|
141
|
+
module_function
|
|
142
|
+
|
|
143
|
+
def clean(backtrace)
|
|
144
|
+
lines = Array(backtrace)
|
|
145
|
+
app = lines.reject { |l| l.to_s.start_with?(GEM_ROOT) || l.to_s.include?("/gems/") || l.to_s.include?("/ruby/3") }
|
|
146
|
+
(app.empty? ? lines : app).first(10)
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|