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,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# A worked example, so `constable test` does something the moment you install the
|
|
4
|
+
# gem. Delete it once you have a real case file -- or keep it as a scratchpad for
|
|
5
|
+
# checking that the harness itself still boots.
|
|
6
|
+
#
|
|
7
|
+
# The vocabulary is the API:
|
|
8
|
+
#
|
|
9
|
+
# investigate a test (it / def test_) -- a plain string description
|
|
10
|
+
# witness a fixture (let) -- memoized per test, never per process
|
|
11
|
+
# briefing setup (before / setup) -- runs before every investigation
|
|
12
|
+
# docket grouping (nested describe) -- scoping sugar, no shared state
|
|
13
|
+
# attest an assertion (expect) -- sugar over assert_*, which still works
|
|
14
|
+
class ExampleCase < UnitCase
|
|
15
|
+
# A witness is memoized inside one investigation and thrown away at the end of
|
|
16
|
+
# it. Name it five times in a test, pay for it once; the next test builds its
|
|
17
|
+
# own. That boundary is the whole reason there is no cross-test cache.
|
|
18
|
+
witness(:badge) { { number: 1, name: "Constable" } }
|
|
19
|
+
|
|
20
|
+
# A briefing runs before *every* investigation below, against a brand-new
|
|
21
|
+
# instance. There is no before(:all) to reach for, which is why nothing an
|
|
22
|
+
# investigation does here can reach the next one.
|
|
23
|
+
briefing do
|
|
24
|
+
@notes = []
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
investigate "runs the moment the gem is installed" do
|
|
28
|
+
@notes << badge[:name]
|
|
29
|
+
|
|
30
|
+
attest(@notes).to eq(["Constable"])
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
investigate "hands the next investigation a clean slate" do
|
|
34
|
+
# @notes was emptied by the briefing again, and the previous investigation's
|
|
35
|
+
# push is nowhere to be found. This passes in any order, on any machine.
|
|
36
|
+
attest(@notes).to be_empty
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Dockets group related investigations and scope setup to the group. They
|
|
40
|
+
# introduce no shared state -- each investigation inside still gets its own
|
|
41
|
+
# fresh instance, its own briefings, its own witnesses.
|
|
42
|
+
docket "with a nested docket" do
|
|
43
|
+
briefing { @notes << "on the beat" }
|
|
44
|
+
|
|
45
|
+
investigate "inherits the outer briefing before running its own" do
|
|
46
|
+
attest(@notes).to eq(["on the beat"])
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# The assert_* primitives are always available alongside attest. attest is
|
|
51
|
+
# sugar, never the only road.
|
|
52
|
+
investigate "still speaks plain assertions" do
|
|
53
|
+
assert_equal 1, badge[:number]
|
|
54
|
+
assert_includes badge[:name], "Const"
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Custom matchers.
|
|
4
|
+
#
|
|
5
|
+
# `attest(actual).to matcher` is sugar over the plain assert_* primitives, which
|
|
6
|
+
# are always available too -- attest never becomes the only way to say a thing.
|
|
7
|
+
# A matcher block returns truthy to pass:
|
|
8
|
+
#
|
|
9
|
+
# Constable::Matchers.define(:name) { |actual, *args| ... }
|
|
10
|
+
#
|
|
11
|
+
# or returns [passed, message, context] when it can say something more useful
|
|
12
|
+
# than "expected it to match" on failure. `context` is printed under the failure
|
|
13
|
+
# in the run summary, which is where a response body or a record's attributes
|
|
14
|
+
# earn their keep.
|
|
15
|
+
#
|
|
16
|
+
# Everything in test/support/ is loaded by test/case_helper.rb before any case
|
|
17
|
+
# file, so a matcher defined here is available to every case in the suite.
|
|
18
|
+
|
|
19
|
+
Constable::Matchers.define(:be_created) do |response|
|
|
20
|
+
response.status == 201
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
Constable::Matchers.define(:exist) do |model_class, attrs|
|
|
24
|
+
model_class.exists?(attrs)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# The richer form, for when the failure message is the difference between a
|
|
28
|
+
# five-second fix and a five-minute one:
|
|
29
|
+
#
|
|
30
|
+
# Constable::Matchers.define(:have_role) do |user, role|
|
|
31
|
+
# [
|
|
32
|
+
# user.roles.include?(role),
|
|
33
|
+
# "expected #{user.class}##{user.id} to have role #{role.inspect}",
|
|
34
|
+
# { roles: user.roles }
|
|
35
|
+
# ]
|
|
36
|
+
# end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Constable's linter.
|
|
2
|
+
#
|
|
3
|
+
# Nondeterminism is caught here, at edit time, rather than discovered in CI three
|
|
4
|
+
# weeks later: bare `sleep`, unfrozen Time.now/Date.today, unstubbed network
|
|
5
|
+
# calls, shared mutable state, conditional assertions, retry helpers.
|
|
6
|
+
#
|
|
7
|
+
# The cops are scoped to native Constable::Case files only. A file inheriting
|
|
8
|
+
# from Constable::ColdCase::* is exempt by design -- cold cases are how a legacy
|
|
9
|
+
# suite adopts Constable without a rewrite, and holding them to native rules on
|
|
10
|
+
# day one would defeat that entirely.
|
|
11
|
+
require:
|
|
12
|
+
- rubocop-constable
|
metadata
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: constable-rails
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Ray Hughes
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: exe
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-09-07 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: activesupport
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '7.0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '7.0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: parser
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - ">="
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '3.1'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '3.1'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: railties
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - ">="
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '7.0'
|
|
48
|
+
type: :runtime
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - ">="
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '7.0'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: sqlite3
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - ">="
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '1.6'
|
|
62
|
+
type: :runtime
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - ">="
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '1.6'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: thor
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - ">="
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '1.2'
|
|
76
|
+
type: :runtime
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - ">="
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '1.2'
|
|
83
|
+
description: |
|
|
84
|
+
Constable replaces RSpec/Minitest for Rails apps that want tests to be fast and
|
|
85
|
+
non-flaky by construction. Every test is isolated by default, nondeterminism is a
|
|
86
|
+
lint error rather than a CI surprise, and every escape hatch is reported until
|
|
87
|
+
someone deals with it. Existing RSpec and Minitest suites adopt it with a one-line
|
|
88
|
+
change per file and zero rewriting -- cold cases run verbatim through their original
|
|
89
|
+
engine while native cases run under strict rules, side by side in one run.
|
|
90
|
+
|
|
91
|
+
Ships with a case-file DSL (investigate/witness/briefing/docket), transactional
|
|
92
|
+
isolation, flake history with rename-surviving content-hash identity, a jail and
|
|
93
|
+
parole docket for legacy red suites, warrants for automatic flake detection, diff-based
|
|
94
|
+
coverage, parallel workers, git-diff test selection, and a RuboCop extension.
|
|
95
|
+
|
|
96
|
+
The gem is published as "constable-rails"; everything inside it -- the module, the
|
|
97
|
+
CLI, the config directory -- is simply "constable".
|
|
98
|
+
email:
|
|
99
|
+
- r.hughes2136@gmail.com
|
|
100
|
+
executables:
|
|
101
|
+
- constable
|
|
102
|
+
extensions: []
|
|
103
|
+
extra_rdoc_files: []
|
|
104
|
+
files:
|
|
105
|
+
- CHANGELOG.md
|
|
106
|
+
- LICENSE.txt
|
|
107
|
+
- README.md
|
|
108
|
+
- exe/constable
|
|
109
|
+
- lib/constable-rails.rb
|
|
110
|
+
- lib/constable.rb
|
|
111
|
+
- lib/constable/case.rb
|
|
112
|
+
- lib/constable/cli.rb
|
|
113
|
+
- lib/constable/cold_case.rb
|
|
114
|
+
- lib/constable/cold_case/minitest.rb
|
|
115
|
+
- lib/constable/cold_case/rspec.rb
|
|
116
|
+
- lib/constable/config.rb
|
|
117
|
+
- lib/constable/coverage.rb
|
|
118
|
+
- lib/constable/diff.rb
|
|
119
|
+
- lib/constable/dsl.rb
|
|
120
|
+
- lib/constable/identity.rb
|
|
121
|
+
- lib/constable/importer.rb
|
|
122
|
+
- lib/constable/importer/modernizer.rb
|
|
123
|
+
- lib/constable/importer/reopener.rb
|
|
124
|
+
- lib/constable/investigation.rb
|
|
125
|
+
- lib/constable/isolation.rb
|
|
126
|
+
- lib/constable/jail.rb
|
|
127
|
+
- lib/constable/log_router.rb
|
|
128
|
+
- lib/constable/matchers.rb
|
|
129
|
+
- lib/constable/order_audit.rb
|
|
130
|
+
- lib/constable/rails_support.rb
|
|
131
|
+
- lib/constable/railtie.rb
|
|
132
|
+
- lib/constable/registry.rb
|
|
133
|
+
- lib/constable/reporter.rb
|
|
134
|
+
- lib/constable/result.rb
|
|
135
|
+
- lib/constable/runner.rb
|
|
136
|
+
- lib/constable/selection.rb
|
|
137
|
+
- lib/constable/storage.rb
|
|
138
|
+
- lib/constable/storage/adapter.rb
|
|
139
|
+
- lib/constable/storage/mysql_adapter.rb
|
|
140
|
+
- lib/constable/storage/postgres_adapter.rb
|
|
141
|
+
- lib/constable/storage/sqlite_adapter.rb
|
|
142
|
+
- lib/constable/version.rb
|
|
143
|
+
- lib/constable/warrants.rb
|
|
144
|
+
- lib/generators/constable/base.rb
|
|
145
|
+
- lib/generators/constable/channel/channel_generator.rb
|
|
146
|
+
- lib/generators/constable/channel/templates/channel_case.rb.tt
|
|
147
|
+
- lib/generators/constable/controller/controller_generator.rb
|
|
148
|
+
- lib/generators/constable/controller/templates/controller_case.rb.tt
|
|
149
|
+
- lib/generators/constable/generator/generator_generator.rb
|
|
150
|
+
- lib/generators/constable/generator/templates/generator_case.rb.tt
|
|
151
|
+
- lib/generators/constable/helper/helper_generator.rb
|
|
152
|
+
- lib/generators/constable/helper/templates/helper_case.rb.tt
|
|
153
|
+
- lib/generators/constable/import_generator.rb
|
|
154
|
+
- lib/generators/constable/install_generator.rb
|
|
155
|
+
- lib/generators/constable/integration/integration_generator.rb
|
|
156
|
+
- lib/generators/constable/integration/templates/request_case.rb.tt
|
|
157
|
+
- lib/generators/constable/job/job_generator.rb
|
|
158
|
+
- lib/generators/constable/job/templates/job_case.rb.tt
|
|
159
|
+
- lib/generators/constable/mailbox/mailbox_generator.rb
|
|
160
|
+
- lib/generators/constable/mailbox/templates/mailbox_case.rb.tt
|
|
161
|
+
- lib/generators/constable/mailer/mailer_generator.rb
|
|
162
|
+
- lib/generators/constable/mailer/templates/mailer_case.rb.tt
|
|
163
|
+
- lib/generators/constable/mailer/templates/preview.rb.tt
|
|
164
|
+
- lib/generators/constable/model/model_generator.rb
|
|
165
|
+
- lib/generators/constable/model/templates/model_case.rb.tt
|
|
166
|
+
- lib/generators/constable/resource/resource_generator.rb
|
|
167
|
+
- lib/generators/constable/scaffold/scaffold_generator.rb
|
|
168
|
+
- lib/generators/constable/scaffold/templates/api_controller_case.rb.tt
|
|
169
|
+
- lib/generators/constable/scaffold/templates/controller_case.rb.tt
|
|
170
|
+
- lib/generators/constable/scaffold/templates/system_case.rb.tt
|
|
171
|
+
- lib/generators/constable/system/system_generator.rb
|
|
172
|
+
- lib/generators/constable/system/templates/system_case.rb.tt
|
|
173
|
+
- lib/generators/constable/templates/authenticatable.rb.tt
|
|
174
|
+
- lib/generators/constable/templates/case_helper.rb.tt
|
|
175
|
+
- lib/generators/constable/templates/config.yml.tt
|
|
176
|
+
- lib/generators/constable/templates/example_case.rb.tt
|
|
177
|
+
- lib/generators/constable/templates/matchers.rb.tt
|
|
178
|
+
- lib/generators/constable/templates/rubocop.yml.tt
|
|
179
|
+
homepage: https://github.com/Ray-Hughes/constable
|
|
180
|
+
licenses:
|
|
181
|
+
- MIT
|
|
182
|
+
metadata:
|
|
183
|
+
homepage_uri: https://github.com/Ray-Hughes/constable
|
|
184
|
+
source_code_uri: https://github.com/Ray-Hughes/constable
|
|
185
|
+
changelog_uri: https://github.com/Ray-Hughes/constable/blob/main/CHANGELOG.md
|
|
186
|
+
bug_tracker_uri: https://github.com/Ray-Hughes/constable/issues
|
|
187
|
+
documentation_uri: https://github.com/Ray-Hughes/constable#readme
|
|
188
|
+
rubygems_mfa_required: 'true'
|
|
189
|
+
post_install_message:
|
|
190
|
+
rdoc_options: []
|
|
191
|
+
require_paths:
|
|
192
|
+
- lib
|
|
193
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
194
|
+
requirements:
|
|
195
|
+
- - ">="
|
|
196
|
+
- !ruby/object:Gem::Version
|
|
197
|
+
version: 3.1.0
|
|
198
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
199
|
+
requirements:
|
|
200
|
+
- - ">="
|
|
201
|
+
- !ruby/object:Gem::Version
|
|
202
|
+
version: '0'
|
|
203
|
+
requirements: []
|
|
204
|
+
rubygems_version: 3.4.10
|
|
205
|
+
signing_key:
|
|
206
|
+
specification_version: 4
|
|
207
|
+
summary: A strict Rails testing framework where fast and non-flaky are structural,
|
|
208
|
+
not disciplinary.
|
|
209
|
+
test_files: []
|