constable-rails 1.2.0 → 1.3.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 +4 -4
- data/CHANGELOG.md +56 -1
- data/README.md +6 -1
- data/lib/constable/cli.rb +34 -0
- data/lib/constable/cold_case.rb +5 -1
- data/lib/constable/config.rb +19 -0
- data/lib/constable/reporter.rb +31 -1
- data/lib/constable/runner.rb +7 -3
- data/lib/constable/version.rb +1 -1
- data/lib/constable/worker_databases.rb +99 -8
- data/lib/constable.rb +3 -3
- data/lib/generators/constable/templates/case_helper.rb.tt +1 -0
- data/lib/generators/constable/templates/config.yml.tt +15 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: '092c12ca7debd9d093f51e3ce6d90332aef25dcb9c16a5cf79b67f716e15a6a5'
|
|
4
|
+
data.tar.gz: 0b82a9db3e6d66228103161d46635e394591223956aa67a4bc5f929075701954
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9bbf3170f8fd728513e0384d36477d4281535276446c86d8e97b8d49b32d956101d1fe4fe30d19688b9a37f36d8159f8ee3061ebc1b2801a6ad55f718d721d27
|
|
7
|
+
data.tar.gz: 300f8c3ecbfb7075492638c355aceb83fc242808e8296ec21798bea71e23bee98ec6b28ffeaad84a11206fd0e6e65b9771c56a441f4e1d12cc730a969a4340ba
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,60 @@ All notable changes to this project are documented here. This project adheres to
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
+
## [1.3.0]
|
|
9
|
+
|
|
10
|
+
Adoption at scale. Everything here came from installing 1.2.0 into a 1,277-spec-file
|
|
11
|
+
Postgres app.
|
|
12
|
+
|
|
13
|
+
### `worker_databases: reuse`
|
|
14
|
+
|
|
15
|
+
Per-worker databases have been rebuilt from schema on every run since 1.0.0, which is
|
|
16
|
+
what Rails does for `rails test`. It is correct by construction — no drift is possible —
|
|
17
|
+
and it is useless for an app whose schema **cannot** rebuild the database by itself. Any
|
|
18
|
+
app with Postgres custom types is in that position: `CREATE TYPE` has no `schema.rb`
|
|
19
|
+
representation, so a from-scratch load fails on a schema that references a type it never
|
|
20
|
+
defines.
|
|
21
|
+
|
|
22
|
+
```yaml
|
|
23
|
+
worker_databases: schema # schema (default) | reuse | off
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
`reuse` connects to `<database>_<index>` when it is already there and builds it from
|
|
27
|
+
schema only when it is not — checking each database separately, which matters for a
|
|
28
|
+
multi-database app where one may be prepared and another not. "Already there" means
|
|
29
|
+
present *and* holding tables: an empty database is not a prepared one, and running a
|
|
30
|
+
suite against no tables is the worst available outcome.
|
|
31
|
+
|
|
32
|
+
It is also just faster for everyone. A 3,000-line schema is no longer reloaded once per
|
|
33
|
+
worker per run.
|
|
34
|
+
|
|
35
|
+
`off` skips sharding entirely — an explicit serial run, no attempt and no warning.
|
|
36
|
+
|
|
37
|
+
New command, for the one-time setup `reuse` needs:
|
|
38
|
+
|
|
39
|
+
```console
|
|
40
|
+
$ constable prepare # build <database>_0 .. _<N-1>, once
|
|
41
|
+
$ constable prepare --workers 8
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Hundreds of cold cases no longer bury the summary
|
|
45
|
+
|
|
46
|
+
Every cold-case file warns, once per run, so that "12 tests not yet under native rules"
|
|
47
|
+
is never something the suite quietly forgets to mention. At 1,277 files that is four
|
|
48
|
+
thousand lines of the same sentence, and the warnings that actually need a decision — an
|
|
49
|
+
`unsafe` block, a jailed test — are lost inside it.
|
|
50
|
+
|
|
51
|
+
Past ten files they collapse into one line that keeps the numbers, which are the part
|
|
52
|
+
that is supposed to shrink:
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
⚠ 1277 files running as cold cases, 18432 tests — not yet under native rules.
|
|
56
|
+
`constable test --unsafe` runs just these.
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Fewer than ten are still listed individually, and nothing else is ever collapsed.
|
|
60
|
+
|
|
61
|
+
|
|
8
62
|
## [1.2.0]
|
|
9
63
|
|
|
10
64
|
Two bugs found installing 1.1.0 into a large real Postgres app (~3,000-line schema,
|
|
@@ -408,7 +462,8 @@ Initial release.
|
|
|
408
462
|
- Diff-based coverage gate — only lines changed in the current diff are held to the
|
|
409
463
|
threshold. `constable beat` for the full picture, `--html` for a browsable report.
|
|
410
464
|
|
|
411
|
-
[Unreleased]: https://github.com/Ray-Hughes/constable/compare/v1.
|
|
465
|
+
[Unreleased]: https://github.com/Ray-Hughes/constable/compare/v1.3.0...HEAD
|
|
466
|
+
[1.3.0]: https://github.com/Ray-Hughes/constable/compare/v1.2.0...v1.3.0
|
|
412
467
|
[1.2.0]: https://github.com/Ray-Hughes/constable/compare/v1.1.0...v1.2.0
|
|
413
468
|
[1.1.0]: https://github.com/Ray-Hughes/constable/compare/v1.0.0...v1.1.0
|
|
414
469
|
[1.0.0]: https://github.com/Ray-Hughes/constable/compare/v0.1.0...v1.0.0
|
data/README.md
CHANGED
|
@@ -399,6 +399,7 @@ worse than one that resets.
|
|
|
399
399
|
| `constable status` | How the suite is doing over time |
|
|
400
400
|
| `constable beat [--html]` | Coverage: overall %, per-file, the unpatrolled list |
|
|
401
401
|
| `constable history relink OLD NEW` | Carry history across a real body change |
|
|
402
|
+
| `constable prepare [--workers N]` | Build the per-worker test databases `worker_databases: reuse` needs |
|
|
402
403
|
| `constable prune [--dry-run]` | Forget docket rows and warrants for tests that no longer exist |
|
|
403
404
|
| `constable import --from=rspec` | Adopt an existing suite as cold cases |
|
|
404
405
|
| `constable modernize PATH` | Opt-in AST rewrite into the native DSL |
|
|
@@ -409,7 +410,11 @@ Order is randomized every run for native cases, with the seed printed and replay
|
|
|
409
410
|
`--seed`. Cold cases keep their own engine's order. Workers run in parallel by default,
|
|
410
411
|
load-balanced by a cached per-test duration index.
|
|
411
412
|
|
|
412
|
-
Each worker gets **its own database**, built from schema the way `rails test` does it
|
|
413
|
+
Each worker gets **its own database**, built from schema the way `rails test` does it —
|
|
414
|
+
or kept between runs, with `worker_databases: reuse`, which is both faster and the only
|
|
415
|
+
thing that works for an app whose schema cannot rebuild the database by itself (any app
|
|
416
|
+
with Postgres custom types: `CREATE TYPE` has no `schema.rb` representation). Prepare
|
|
417
|
+
those once with `constable prepare`.
|
|
413
418
|
Sharing one would not be a speed/safety trade but a correctness bug: on SQLite the run
|
|
414
419
|
dissolves into `database is locked`, and on a client/server database tests quietly see
|
|
415
420
|
each other's rows. If your app has ActiveRecord but cannot shard, Constable runs serially
|
data/lib/constable/cli.rb
CHANGED
|
@@ -399,6 +399,40 @@ module Constable
|
|
|
399
399
|
end
|
|
400
400
|
end
|
|
401
401
|
|
|
402
|
+
desc "prepare", "Build the per-worker test databases parallel runs need"
|
|
403
|
+
long_desc <<~DESC
|
|
404
|
+
For `worker_databases: reuse`. Creates `<database>_0` .. `<database>_<N-1>` and
|
|
405
|
+
loads the schema into each, once, so later runs can connect straight to them.
|
|
406
|
+
|
|
407
|
+
Only useful for an app that has opted into reuse -- the default `schema` mode
|
|
408
|
+
rebuilds them on every run and needs no preparation. It is the answer for an app
|
|
409
|
+
whose schema cannot rebuild the database by itself: prepare these once, by whatever
|
|
410
|
+
means already works for you, and Constable will use them from then on.
|
|
411
|
+
DESC
|
|
412
|
+
option :workers, type: :numeric, desc: "How many to prepare (default: the configured worker count)"
|
|
413
|
+
def prepare
|
|
414
|
+
config = load_config
|
|
415
|
+
unless WorkerDatabases.shardable?
|
|
416
|
+
warn "This app has no ActiveRecord test databases to prepare."
|
|
417
|
+
exit(EXIT_USAGE)
|
|
418
|
+
end
|
|
419
|
+
|
|
420
|
+
count = (options[:workers] || config.parallel_workers).to_i.clamp(1, 64)
|
|
421
|
+
say "Preparing #{count} worker #{count == 1 ? "database" : "databases"}..."
|
|
422
|
+
|
|
423
|
+
count.times do |index|
|
|
424
|
+
built = WorkerDatabases.prepare!(index)
|
|
425
|
+
say " worker #{index}: #{built.empty? ? "already prepared" : "built #{built.join(", ")}"}"
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
say "\nDone. Set `worker_databases: reuse` so runs connect to these instead of " \
|
|
429
|
+
"rebuilding them."
|
|
430
|
+
0
|
|
431
|
+
rescue Constable::Error => e
|
|
432
|
+
warn e.message
|
|
433
|
+
exit(EXIT_FAILED)
|
|
434
|
+
end
|
|
435
|
+
|
|
402
436
|
desc "prune", "Forget docket rows and warrants for tests that no longer exist"
|
|
403
437
|
long_desc <<~DESC
|
|
404
438
|
A test's key is a content hash of its body, so editing a jailed test gives it a new
|
data/lib/constable/cold_case.rb
CHANGED
|
@@ -195,7 +195,11 @@ module Constable
|
|
|
195
195
|
"running as a cold case (#{base_class_name}) — " \
|
|
196
196
|
"#{count} #{count == 1 ? "test" : "tests"} not yet under native rules",
|
|
197
197
|
location: location,
|
|
198
|
-
kind: :cold_case
|
|
198
|
+
kind: :cold_case,
|
|
199
|
+
# Carried so the reporter can total them when there are too many to list. A
|
|
200
|
+
# suite mid-adoption has hundreds of these, and printing every one buries the
|
|
201
|
+
# things that actually need a decision.
|
|
202
|
+
tests: count
|
|
199
203
|
)
|
|
200
204
|
end
|
|
201
205
|
|
data/lib/constable/config.rb
CHANGED
|
@@ -21,6 +21,7 @@ module Constable
|
|
|
21
21
|
"fail_on_warnings" => false,
|
|
22
22
|
"output" => "concise",
|
|
23
23
|
"parallel_workers" => "auto",
|
|
24
|
+
"worker_databases" => "schema",
|
|
24
25
|
"tiers" => {
|
|
25
26
|
"unit" => "test/cases/models/**/*",
|
|
26
27
|
"integration" => "test/cases/controllers/**/*",
|
|
@@ -103,6 +104,24 @@ module Constable
|
|
|
103
104
|
# but you can see which test is hanging without waiting for the summary.
|
|
104
105
|
#
|
|
105
106
|
# The summary itself is identical either way. This only affects the live stream.
|
|
107
|
+
# How a parallel worker gets a database of its own.
|
|
108
|
+
#
|
|
109
|
+
# schema rebuild `<database>_<index>` from schema on every run. What Rails does for
|
|
110
|
+
# `rails test`, and correct by construction: no drift is possible.
|
|
111
|
+
# reuse connect to `<database>_<index>` when it already exists, and build it from
|
|
112
|
+
# schema only when it does not. Faster -- a large schema is not reloaded on
|
|
113
|
+
# every run -- and the only option that works for an app whose schema cannot
|
|
114
|
+
# rebuild the database by itself, which is any app with Postgres custom
|
|
115
|
+
# types. The cost is that keeping those databases current is now yours.
|
|
116
|
+
# off do not shard, so do not fork. An explicit serial run, with no attempt and
|
|
117
|
+
# no warning.
|
|
118
|
+
WORKER_DATABASE_MODES = %i[schema reuse off].freeze
|
|
119
|
+
|
|
120
|
+
def worker_databases
|
|
121
|
+
mode = @raw["worker_databases"].to_s.strip.downcase.to_sym
|
|
122
|
+
WORKER_DATABASE_MODES.include?(mode) ? mode : :schema
|
|
123
|
+
end
|
|
124
|
+
|
|
106
125
|
OUTPUT_MODES = %i[concise expanded].freeze
|
|
107
126
|
|
|
108
127
|
def output_mode
|
data/lib/constable/reporter.rb
CHANGED
|
@@ -44,6 +44,12 @@ module Constable
|
|
|
44
44
|
# without a limit a quiet case could hold the buffer forever.
|
|
45
45
|
STREAM_FLUSH_THRESHOLD = 12
|
|
46
46
|
|
|
47
|
+
# How many cold-case files are listed one by one before they are summarised instead.
|
|
48
|
+
# A suite part-way through adoption has hundreds -- one real app had 1,277 -- and
|
|
49
|
+
# printing every one buries the warnings that actually need a decision under four
|
|
50
|
+
# thousand lines that say the same thing.
|
|
51
|
+
COLD_CASE_LIST_LIMIT = 10
|
|
52
|
+
|
|
47
53
|
DEFAULT_SLOWEST = 5
|
|
48
54
|
|
|
49
55
|
# Column the expanded stream right-aligns durations into. Descriptions are never
|
|
@@ -609,6 +615,7 @@ module Constable
|
|
|
609
615
|
end
|
|
610
616
|
|
|
611
617
|
def section_warnings(warnings)
|
|
618
|
+
warnings = collapse_cold_cases(warnings)
|
|
612
619
|
return if warnings.empty?
|
|
613
620
|
|
|
614
621
|
section("WARNINGS")
|
|
@@ -624,6 +631,25 @@ module Constable
|
|
|
624
631
|
end
|
|
625
632
|
end
|
|
626
633
|
|
|
634
|
+
# Cold cases are a fact about the suite, not a list of problems: every one says the
|
|
635
|
+
# same sentence about a different file. Past the limit they become one line that
|
|
636
|
+
# still carries the number -- which is the part that is supposed to shrink over time,
|
|
637
|
+
# and the reason the warning exists at all.
|
|
638
|
+
def collapse_cold_cases(warnings)
|
|
639
|
+
cold, rest = warnings.partition { |w| w[:kind] == :cold_case }
|
|
640
|
+
return warnings if cold.size <= COLD_CASE_LIST_LIMIT
|
|
641
|
+
|
|
642
|
+
tests = cold.sum { |w| w[:tests].to_i }
|
|
643
|
+
total = tests.positive? ? ", #{tests} #{pluralize(tests, "test")}" : ""
|
|
644
|
+
|
|
645
|
+
[{
|
|
646
|
+
kind: :cold_case,
|
|
647
|
+
location: nil,
|
|
648
|
+
message: "#{cold.size} files running as cold cases#{total} — not yet under " \
|
|
649
|
+
"native rules. `constable test --unsafe` runs just these."
|
|
650
|
+
}] + rest
|
|
651
|
+
end
|
|
652
|
+
|
|
627
653
|
# A warning carries the author's own words -- an unsafe block's reason, a cold case's
|
|
628
654
|
# count -- and those are easily ninety columns. Wrapped to the frame, but respecting
|
|
629
655
|
# any line breaks the message already chose.
|
|
@@ -707,7 +733,11 @@ module Constable
|
|
|
707
733
|
w = warning.to_h.transform_keys(&:to_sym)
|
|
708
734
|
return nil if w[:message].nil?
|
|
709
735
|
|
|
710
|
-
|
|
736
|
+
# `tests` rides along so cold cases can be totalled when there are too many to list.
|
|
737
|
+
# Normalizing is about the three fields the reporter needs, not about discarding
|
|
738
|
+
# everything a warning chose to carry.
|
|
739
|
+
{ message: w[:message].to_s, location: w[:location], kind: (w[:kind] || :unsafe).to_sym,
|
|
740
|
+
tests: w[:tests] }.compact
|
|
711
741
|
end
|
|
712
742
|
|
|
713
743
|
def normalize_coverage(coverage)
|
data/lib/constable/runner.rb
CHANGED
|
@@ -283,6 +283,9 @@ module Constable
|
|
|
283
283
|
#
|
|
284
284
|
# When we cannot shard, we run serially and say why. Slow is a trade-off; wrong is not.
|
|
285
285
|
def parallel_safe?
|
|
286
|
+
# An explicit opt-out. No attempt, and no warning about one -- the user has already
|
|
287
|
+
# told us they know.
|
|
288
|
+
return false if @config.worker_databases == :off
|
|
286
289
|
return true unless WorkerDatabases.active_record?
|
|
287
290
|
return true if WorkerDatabases.shardable?
|
|
288
291
|
|
|
@@ -342,7 +345,7 @@ module Constable
|
|
|
342
345
|
# A raise here dumps a full stack trace per worker and leaves the parent
|
|
343
346
|
# reporting a run that never happened.
|
|
344
347
|
begin
|
|
345
|
-
WorkerDatabases.after_fork!(worker_index)
|
|
348
|
+
WorkerDatabases.after_fork!(worker_index, mode: @config.worker_databases)
|
|
346
349
|
rescue Constable::Error => e
|
|
347
350
|
write_message(writer, :worker_error, e.message)
|
|
348
351
|
writer.close
|
|
@@ -400,8 +403,9 @@ module Constable
|
|
|
400
403
|
"no parallel worker could build its own test database, so the suite ran serially " \
|
|
401
404
|
"instead. This usually means the app's schema cannot rebuild the database by " \
|
|
402
405
|
"itself -- Postgres custom types, functions and triggers are the common reason, " \
|
|
403
|
-
"and `rails test` parallelization fails the same way. Set
|
|
404
|
-
"to
|
|
406
|
+
"and `rails test` parallelization fails the same way. Set " \
|
|
407
|
+
"`worker_databases: reuse` to keep prepared databases between runs instead, or " \
|
|
408
|
+
"`worker_databases: off` to stop trying. The first worker said: #{reason}",
|
|
405
409
|
kind: :parallel
|
|
406
410
|
)
|
|
407
411
|
|
data/lib/constable/version.rb
CHANGED
|
@@ -46,24 +46,115 @@ module Constable
|
|
|
46
46
|
false
|
|
47
47
|
end
|
|
48
48
|
|
|
49
|
-
# Child side, immediately after the fork and before any test runs.
|
|
50
|
-
# `<database>_<index
|
|
49
|
+
# Child side, immediately after the fork and before any test runs. Gives this process
|
|
50
|
+
# a database of its own, named `<database>_<index>`.
|
|
51
51
|
#
|
|
52
|
-
#
|
|
53
|
-
#
|
|
54
|
-
|
|
52
|
+
# `mode` is the `worker_databases` setting:
|
|
53
|
+
#
|
|
54
|
+
# :schema rebuild it from schema every run, which is what Rails does. Correct by
|
|
55
|
+
# construction -- no drift is possible -- and the right default.
|
|
56
|
+
# :reuse connect to it when it is already there, build it from schema when it is
|
|
57
|
+
# not. Skips reloading a large schema on every run, and is the only thing
|
|
58
|
+
# that works when the schema cannot rebuild the database by itself.
|
|
59
|
+
def after_fork!(index, mode: :schema)
|
|
55
60
|
return false unless shardable?
|
|
61
|
+
return reuse!(index) if mode.to_sym == :reuse
|
|
56
62
|
|
|
57
63
|
::ActiveRecord::TestDatabases.create_and_load_schema(index, env_name: env_name)
|
|
58
64
|
true
|
|
59
65
|
rescue StandardError => e
|
|
60
|
-
# A worker that cannot build its own database
|
|
61
|
-
#
|
|
62
|
-
#
|
|
66
|
+
# A worker that cannot build its own database must never fall back to sharing the
|
|
67
|
+
# parent's -- that is the corruption this module exists to prevent. The runner turns
|
|
68
|
+
# this into a serial run rather than letting it kill the process.
|
|
63
69
|
raise Constable::Error, "worker #{index} could not create its own test database: " \
|
|
64
70
|
"#{e.class}: #{e.message}"
|
|
65
71
|
end
|
|
66
72
|
|
|
73
|
+
# `constable prepare`. Builds worker `index`'s databases from the parent process --
|
|
74
|
+
# the same work :reuse would do lazily inside a fork, done once, deliberately, where
|
|
75
|
+
# the output is visible and a failure is not four stack traces at once.
|
|
76
|
+
def prepare!(index)
|
|
77
|
+
raise Constable::Error, "this app has no ActiveRecord databases to prepare" unless shardable?
|
|
78
|
+
|
|
79
|
+
# Renaming is destructive, and here it happens in the parent rather than in a fork
|
|
80
|
+
# that is about to die. Without restoring the names afterwards this command would
|
|
81
|
+
# leave the process -- and, worse, anything else in it -- pointed at
|
|
82
|
+
# `<database>_<index>` instead of the real test database.
|
|
83
|
+
original = database_names
|
|
84
|
+
begin
|
|
85
|
+
reuse!(index)
|
|
86
|
+
ensure
|
|
87
|
+
restore_database_names(original)
|
|
88
|
+
::ActiveRecord::Base.establish_connection
|
|
89
|
+
end
|
|
90
|
+
rescue Constable::Error
|
|
91
|
+
raise
|
|
92
|
+
rescue StandardError => e
|
|
93
|
+
raise Constable::Error, "could not prepare worker #{index}: #{e.class}: #{e.message}"
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def database_names
|
|
97
|
+
::ActiveRecord::Base.configurations
|
|
98
|
+
.configs_for(env_name: env_name, include_hidden: true)
|
|
99
|
+
.map(&:database)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def restore_database_names(names)
|
|
103
|
+
::ActiveRecord::Base.configurations
|
|
104
|
+
.configs_for(env_name: env_name, include_hidden: true)
|
|
105
|
+
.zip(names).each { |config, name| config._database = name if name }
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# The :reuse half. Points every database this environment declares at its `_<index>`
|
|
109
|
+
# sibling, and only builds the ones that are not there yet.
|
|
110
|
+
#
|
|
111
|
+
# "There" means present *and* populated: an empty database is not a prepared one, and
|
|
112
|
+
# connecting to it would hand the worker a suite with no tables. Deciding that per
|
|
113
|
+
# database rather than per worker matters for a multi-database app -- Caseflow has a
|
|
114
|
+
# primary and an ETL database -- where one may be prepared and the other not.
|
|
115
|
+
#
|
|
116
|
+
# Keeping these current is the user's job once they opt in, which is the trade the
|
|
117
|
+
# setting exists to let them make.
|
|
118
|
+
def reuse!(index)
|
|
119
|
+
built = []
|
|
120
|
+
|
|
121
|
+
each_worker_config(index) do |db_config|
|
|
122
|
+
next if populated?(db_config)
|
|
123
|
+
|
|
124
|
+
::ActiveRecord::Tasks::DatabaseTasks.reconstruct_from_schema(db_config, nil)
|
|
125
|
+
built << db_config.database
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
built
|
|
129
|
+
ensure
|
|
130
|
+
# Rails does this after its own schema load: the pool has to be re-established
|
|
131
|
+
# against the renamed configuration before any test asks for a connection.
|
|
132
|
+
::ActiveRecord::Base.establish_connection
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Every database config for this environment, renamed to its per-worker sibling.
|
|
136
|
+
# `_database=` is exactly how Rails' own TestDatabases does the renaming, and this
|
|
137
|
+
# runs in a forked child, so the mutation dies with the worker.
|
|
138
|
+
def each_worker_config(index)
|
|
139
|
+
configs = ::ActiveRecord::Base.configurations.configs_for(env_name: env_name,
|
|
140
|
+
include_hidden: true)
|
|
141
|
+
configs.each do |db_config|
|
|
142
|
+
db_config._database = "#{db_config.database}_#{index}"
|
|
143
|
+
next unless db_config.database_tasks?
|
|
144
|
+
|
|
145
|
+
yield db_config
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Present and holding tables. A database that exists but is empty is not prepared, and
|
|
150
|
+
# silently running a suite against no tables is the worst of the available outcomes.
|
|
151
|
+
def populated?(db_config)
|
|
152
|
+
::ActiveRecord::Base.establish_connection(db_config)
|
|
153
|
+
::ActiveRecord::Base.connection.tables.any?
|
|
154
|
+
rescue StandardError
|
|
155
|
+
false
|
|
156
|
+
end
|
|
157
|
+
|
|
67
158
|
def env_name
|
|
68
159
|
if defined?(::ActiveRecord::ConnectionHandling::DEFAULT_ENV)
|
|
69
160
|
::ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
|
data/lib/constable.rb
CHANGED
|
@@ -97,8 +97,8 @@ module Constable
|
|
|
97
97
|
|
|
98
98
|
# Warnings are never silent and never fatal by default. They accumulate through a run
|
|
99
99
|
# and always get their own section in the summary.
|
|
100
|
-
def warn!(message, location: nil, kind: :unsafe)
|
|
101
|
-
warnings << { message: message, location: location, kind: kind }
|
|
100
|
+
def warn!(message, location: nil, kind: :unsafe, **extra)
|
|
101
|
+
warnings << { message: message, location: location, kind: kind, **extra }
|
|
102
102
|
end
|
|
103
103
|
|
|
104
104
|
def warnings
|
|
@@ -149,7 +149,7 @@ module Constable
|
|
|
149
149
|
SETTINGS = %i[
|
|
150
150
|
cold_cases warrants warrant_retries auto_relink parole_period
|
|
151
151
|
coverage coverage_threshold coverage_html fail_on_warnings parallel_workers
|
|
152
|
-
output tiers
|
|
152
|
+
worker_databases output tiers
|
|
153
153
|
].freeze
|
|
154
154
|
|
|
155
155
|
# `storage` is the one setting that cannot live here, and the reason is ordering, not
|
|
@@ -156,6 +156,7 @@ end
|
|
|
156
156
|
#
|
|
157
157
|
# c.cold_cases = [] # RSpec/Minitest globs to run as cold cases
|
|
158
158
|
# c.parallel_workers = "auto" # or an integer. Each worker gets its own database
|
|
159
|
+
# c.worker_databases = :schema # or :reuse (keep them between runs) / :off
|
|
159
160
|
# c.output = :concise # or :expanded -- a line per test, with timings
|
|
160
161
|
# c.fail_on_warnings = false # CI: fail when the warning count is not trending down
|
|
161
162
|
# c.warrants = false # rerun a failure in isolation before believing it
|
|
@@ -73,6 +73,21 @@ fail_on_warnings: false # CI: fail the build when the warning count doesn't tre
|
|
|
73
73
|
|
|
74
74
|
parallel_workers: auto # or an explicit integer
|
|
75
75
|
|
|
76
|
+
# How a parallel worker gets a database of its own. Sharing one is not a speed/safety
|
|
77
|
+
# trade but a correctness bug: on SQLite the run dissolves into "database is locked", and
|
|
78
|
+
# on a client/server database tests quietly see each other's rows.
|
|
79
|
+
#
|
|
80
|
+
# schema rebuild <database>_<index> from schema on every run. What Rails does for
|
|
81
|
+
# `rails test`, and correct by construction -- no drift is possible.
|
|
82
|
+
# reuse connect to <database>_<index> when it is already there, and build it from
|
|
83
|
+
# schema only when it is not. Faster, because a large schema is not reloaded
|
|
84
|
+
# every run -- and the only option that works at all when the schema cannot
|
|
85
|
+
# rebuild the database by itself, which is true of any app with Postgres custom
|
|
86
|
+
# types (`CREATE TYPE` has no schema.rb representation, so a from-scratch load
|
|
87
|
+
# fails). Keeping those databases current becomes your job.
|
|
88
|
+
# off do not shard, so do not fork. An explicit serial run: no attempt, no warning.
|
|
89
|
+
worker_databases: schema # schema | reuse | off
|
|
90
|
+
|
|
76
91
|
# How much the live stream says while the suite is running. The summary is identical
|
|
77
92
|
# either way -- this only changes what you watch on the way there.
|
|
78
93
|
#
|