pinspec 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 +133 -0
- data/LICENSE.txt +21 -0
- data/README.md +183 -0
- data/exe/pinspec +8 -0
- data/lib/pinspec/analyzer/app_profile_reader.rb +348 -0
- data/lib/pinspec/analyzer/factory_registry.rb +288 -0
- data/lib/pinspec/analyzer/inflector.rb +85 -0
- data/lib/pinspec/analyzer/schema_reader.rb +444 -0
- data/lib/pinspec/analyzer/source.rb +56 -0
- data/lib/pinspec/analyzer/target_parser.rb +710 -0
- data/lib/pinspec/cli.rb +585 -0
- data/lib/pinspec/emit/namer.rb +103 -0
- data/lib/pinspec/emit/spec_writer.rb +504 -0
- data/lib/pinspec/emit/stability_filter.rb +183 -0
- data/lib/pinspec/errors.rb +85 -0
- data/lib/pinspec/inputs/boundary.rb +112 -0
- data/lib/pinspec/inputs/corpus.rb +148 -0
- data/lib/pinspec/inputs/hydrator.rb +197 -0
- data/lib/pinspec/inputs/redactor.rb +138 -0
- data/lib/pinspec/inputs/sample_runner.rb +98 -0
- data/lib/pinspec/inputs/sampler.rb +187 -0
- data/lib/pinspec/report/summary.rb +348 -0
- data/lib/pinspec/runner/capture.rb +127 -0
- data/lib/pinspec/runner/probe_generator.rb +662 -0
- data/lib/pinspec/runner/sandbox.rb +121 -0
- data/lib/pinspec/setup/context_builder.rb +471 -0
- data/lib/pinspec/setup/dependency_resolver.rb +236 -0
- data/lib/pinspec/tags.rb +103 -0
- data/lib/pinspec/types.rb +497 -0
- data/lib/pinspec/validate/mutation_adapter.rb +108 -0
- data/lib/pinspec/validate/pin_scorer.rb +164 -0
- data/lib/pinspec/verify/verifier.rb +149 -0
- data/lib/pinspec/version.rb +8 -0
- data/lib/pinspec.rb +17 -0
- data/templates/factory_build.rb +54 -0
- data/templates/serializer.rb +243 -0
- data/templates/spec_support.rb +83 -0
- metadata +134 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 7f80ae1282baf0c4d24f694ced4a28e3333bd2db013a6b5a50c64dbf24349e51
|
|
4
|
+
data.tar.gz: 3a970d963883ed690524562afa726ab0690aa6f5325bd3e65400c267b34ffb0f
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: e8d0773aa7d21161ba1a6b2c2cd64634dc0edeaca7bd9b089e92db7ecb76b9cc77e091316f5a97fa008b25cd790dd8e155777eede702138f14e08ebd9019d93f
|
|
7
|
+
data.tar.gz: 76952dd81076d9e47aef99bbea8b181c6946312127a1360ea3ccd8ba7f0bbccfb25a511104a741292c920924e04803c35eb7d1f19e9187180954859f51430523
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0 - 2026-08-13
|
|
4
|
+
|
|
5
|
+
First release.
|
|
6
|
+
|
|
7
|
+
`pinspec pin FILE#METHOD --app PATH` resolves a Rails service object or model
|
|
8
|
+
method, builds the world it needs, runs it inside the application, writes an RSpec
|
|
9
|
+
file that freezes what it observed, and verifies that file in the application's own
|
|
10
|
+
test environment.
|
|
11
|
+
|
|
12
|
+
### Commands
|
|
13
|
+
|
|
14
|
+
- `analyze` - application profile, schema, factories and hazards. Static: no boot,
|
|
15
|
+
no database connection.
|
|
16
|
+
- `plan` - the world pinspec would build and the arguments it would pass. Pure data,
|
|
17
|
+
readable before anything runs.
|
|
18
|
+
- `capture` - runs the generated probe in the application and writes
|
|
19
|
+
`observations.json`.
|
|
20
|
+
- `pin` - capture, emit the spec, verify it.
|
|
21
|
+
- `validate` - mutation-scores a pin one aspect at a time. Requires Ruby >= 3.4 and
|
|
22
|
+
`mutineer`; refuses with an explanation otherwise.
|
|
23
|
+
- `report` - the markdown report from the last run.
|
|
24
|
+
|
|
25
|
+
### Analysis
|
|
26
|
+
|
|
27
|
+
- Targets resolve from a static parse across six construction shapes: `new`, class
|
|
28
|
+
method, interactor, `dry_initializer`, `Struct` and model instance, including one
|
|
29
|
+
level of superclass inheritance. Constructor arguments are part of the invocation
|
|
30
|
+
surface, so a zero-argument `#call` whose dependencies arrive via `initialize` is
|
|
31
|
+
supported.
|
|
32
|
+
- `db/schema.rb` is parsed into tables, columns, indexes and a foreign-key map with
|
|
33
|
+
provenance: a database constraint, a declared association, or an inferred `*_id`
|
|
34
|
+
column. Constraints win over inferences, and inferences are reported separately.
|
|
35
|
+
Polymorphic ids are excluded.
|
|
36
|
+
- Association targets resolve against the schema's real table names rather than by
|
|
37
|
+
pluralising, and tables behind an engine prefix are found through the class a
|
|
38
|
+
factory declares - so `Spree::Order` resolves to `spree_orders`.
|
|
39
|
+
- factory_bot and factory_girl definitions are indexed from source and never
|
|
40
|
+
executed, including traits, inheritance through nesting and `parent:`, callbacks,
|
|
41
|
+
and factories that never persist.
|
|
42
|
+
- Gem, auth, tenancy, soft-delete, versioning, feature-flag, attachment and
|
|
43
|
+
test-harness detection from `Gemfile.lock` and the helper files, with per-model
|
|
44
|
+
hazards (`default_scope`, `after_commit`, attachments, `acts_as_tenant`) reported
|
|
45
|
+
by file and line.
|
|
46
|
+
- Unknown schema statements and column types are recorded rather than fatal.
|
|
47
|
+
|
|
48
|
+
### Planning and inputs
|
|
49
|
+
|
|
50
|
+
- A `SetupPlan` is pure data: no connection, no application code, no application
|
|
51
|
+
process. Its id is content-addressed, so the same target always yields the same
|
|
52
|
+
plan and a changed plan is visibly different.
|
|
53
|
+
- The environment is pinned before any record exists: frozen clock, seeded
|
|
54
|
+
randomness, locale, time zone.
|
|
55
|
+
- A factory is used where one exists and is then left to own its own associations;
|
|
56
|
+
otherwise the required `belongs_to` closure is built from the schema. Nullable
|
|
57
|
+
associations are left null.
|
|
58
|
+
- A current-user record is built only for a target that could observe one.
|
|
59
|
+
- Boundary values and one-factor-at-a-time variation across constructor and method
|
|
60
|
+
parameters, interleaved so a small case budget reaches both. One case omits an
|
|
61
|
+
optional argument entirely, exercising the method's own default expression.
|
|
62
|
+
- `--sample` reads real rows through a generated read-only script in the
|
|
63
|
+
application's runtime, then rebuilds them as `create!` calls. Personal data is
|
|
64
|
+
rewritten preserving domain and length; row sources are hashed.
|
|
65
|
+
|
|
66
|
+
### Capture
|
|
67
|
+
|
|
68
|
+
- The probe is generated, stdlib-only, and held to a Ruby 2.6 syntax floor, so it
|
|
69
|
+
runs in the application's Ruby. It can be read before it is run.
|
|
70
|
+
- It refuses to run outside `RAILS_ENV=test`, and honours the suite's isolation
|
|
71
|
+
regime - rolling back per case, or truncating where the suite does not transact.
|
|
72
|
+
- No database id reaches a snapshot. Foreign keys pointing at plan-built records
|
|
73
|
+
become refs; other id-shaped values become a wildcard. Ids inside job arguments
|
|
74
|
+
and GlobalID strings are resolved too.
|
|
75
|
+
- Side-effect sinks are forced to `:test` and cleared after setup, so a factory
|
|
76
|
+
callback's enqueue is never attributed to the target.
|
|
77
|
+
- Two boots by default, because one process shares warm caches and an input-keyed
|
|
78
|
+
memo would agree with itself. Cases that differ between boots are reported with a
|
|
79
|
+
named cause rather than pinned.
|
|
80
|
+
|
|
81
|
+
### Emitted specs
|
|
82
|
+
|
|
83
|
+
- The spec forces the capture's answer on every axis rather than inheriting the
|
|
84
|
+
suite's: isolation regime, queue adapter, clock, seed, locale and zone.
|
|
85
|
+
- Verification runs in three environments - the file alone, a hostile timezone and
|
|
86
|
+
locale with a different seed, and the file twice in one process.
|
|
87
|
+
- Job and mail assertions are block-scoped. A timezone guard is emitted only for a
|
|
88
|
+
target that reads the process clock.
|
|
89
|
+
- pinspec overwrites only its own output; anything else needs `--force`.
|
|
90
|
+
- Example names are deterministic. No captured value is sent anywhere: there is no
|
|
91
|
+
path from the CLI to a language model.
|
|
92
|
+
|
|
93
|
+
### Refusals
|
|
94
|
+
|
|
95
|
+
Each carries its own exit code: a target that takes a block, a constructor that
|
|
96
|
+
resolves its own dependencies, a parameter naming a model the application has no
|
|
97
|
+
table, model or factory for, mutually required NOT NULL foreign keys, a NOT NULL
|
|
98
|
+
column whose type has no honest value, attachments, `ros-apartment` tenancy, an
|
|
99
|
+
ambiguous target name, a delegation or `method_missing` redirect,
|
|
100
|
+
`db/structure.sql`, Rails below 6.0, and nothing stable to pin.
|
|
101
|
+
|
|
102
|
+
pinspec does not pass `nil` for a model it could not build: the target would raise
|
|
103
|
+
on nil and that error would be pinned as though the application produced it.
|
|
104
|
+
|
|
105
|
+
### Known limitations
|
|
106
|
+
|
|
107
|
+
- Mutation scores on real service objects are frequently weak. The default corpus
|
|
108
|
+
builds the smallest world that can exist, and the smallest world often does not
|
|
109
|
+
reach a target's branches. Read the scores before trusting a pin; `--sample`
|
|
110
|
+
helps.
|
|
111
|
+
- `after_commit` never fires under transactional isolation, in the capture or in the
|
|
112
|
+
emitted spec. pinspec does not fake it, and the report says so.
|
|
113
|
+
- Per-case rollback covers the primary writing connection only.
|
|
114
|
+
- Attachments are refused rather than run against an empty blob.
|
|
115
|
+
- Clock and attribute-read detection scans the target's own file, so a transitive
|
|
116
|
+
callee is invisible. No warning is not proof of no read.
|
|
117
|
+
- `insta` and `approvals` snapshot backends are not implemented; `--snapshot`
|
|
118
|
+
refuses them rather than silently emitting inline literals.
|
|
119
|
+
|
|
120
|
+
### Requirements
|
|
121
|
+
|
|
122
|
+
Ruby >= 3.2 for the CLI, and >= 3.4 for `validate`. Rails >= 6.0 in the target
|
|
123
|
+
application. No database gem is added: everything that touches application data
|
|
124
|
+
runs inside the application through `rails runner`.
|
|
125
|
+
|
|
126
|
+
### Verified
|
|
127
|
+
|
|
128
|
+
658 examples on Ruby 3.4.6, 3.3.0 and 3.2.2, each under a normal shell and under
|
|
129
|
+
`LANG=C LC_ALL=C TZ=Etc/GMT+8`. Both isolation regimes are covered end to end by
|
|
130
|
+
Rails applications that boot on PostgreSQL, and the six axes on which the probe
|
|
131
|
+
process and the spec process could disagree are held to agreement by a test that
|
|
132
|
+
fails the build. Also exercised against a large open-source Rails application on
|
|
133
|
+
Spree: six service objects pinned, each green in all three verify configurations.
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Rehan Munir
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# pinspec
|
|
2
|
+
|
|
3
|
+
Characterization tests for legacy Rails codebases.
|
|
4
|
+
|
|
5
|
+
Point pinspec at a Rails service object or model method. It works out how to invoke
|
|
6
|
+
it, runs it against your test database, writes an RSpec file that freezes what the
|
|
7
|
+
code does today, and then verifies that file passes in your app's own test
|
|
8
|
+
environment.
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
pinspec pin app/services/invoice_calculator.rb#call --app .
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
capture InvoiceCalculator#call
|
|
16
|
+
runs 2 boots
|
|
17
|
+
stable 2 of 2
|
|
18
|
+
|
|
19
|
+
emitted spec/characterization/invoice_calculator_call_spec.rb
|
|
20
|
+
pinned c001, c002
|
|
21
|
+
aspects 2 return, 2 jobs
|
|
22
|
+
|
|
23
|
+
verify
|
|
24
|
+
isolated green (4 examples)
|
|
25
|
+
hostile green (4 examples)
|
|
26
|
+
neighbored green (4 examples)
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
A pin freezes current behaviour. It is not a claim that the behaviour is correct —
|
|
30
|
+
bugs get pinned on purpose, so a refactor cannot change them silently.
|
|
31
|
+
|
|
32
|
+
## Install
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
gem install pinspec
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Ruby >= 3.2. Rails >= 6.0 in the target app. PostgreSQL, MySQL and SQLite all work:
|
|
39
|
+
pinspec adds no database gem, because everything that touches your data runs inside
|
|
40
|
+
your app through `rails runner`.
|
|
41
|
+
|
|
42
|
+
## Commands
|
|
43
|
+
|
|
44
|
+
| Command | What it does |
|
|
45
|
+
| --- | --- |
|
|
46
|
+
| `pinspec analyze [APP]` | App profile, schema, factories and hazards. Reads files only — no boot, no database. |
|
|
47
|
+
| `pinspec plan FILE#METHOD --app PATH` | The world it would build, and the arguments it would pass. Still no execution. |
|
|
48
|
+
| `pinspec capture FILE#METHOD --app PATH` | Runs the probe in your app, writes `observations.json`. |
|
|
49
|
+
| `pinspec pin FILE#METHOD --app PATH` | Capture, emit the spec, verify it. |
|
|
50
|
+
| `pinspec validate FILE#METHOD --app PATH` | Mutation-scores the pin, one aspect at a time. Needs Ruby >= 3.4 and `mutineer`. |
|
|
51
|
+
| `pinspec report --app PATH` | Prints the last run's markdown report. |
|
|
52
|
+
|
|
53
|
+
Useful flags: `--cases N`, `--boots N`, `--sample` (read real rows from your
|
|
54
|
+
development database), `--no-redact`, `--force`, `--app-env KEY=VALUE`.
|
|
55
|
+
|
|
56
|
+
The target comes first. `--app-env` is an array option and will swallow it otherwise.
|
|
57
|
+
|
|
58
|
+
## Verification
|
|
59
|
+
|
|
60
|
+
Every pin is checked in three environments, because one run on the machine that
|
|
61
|
+
captured it proves repeatability rather than portability:
|
|
62
|
+
|
|
63
|
+
- **isolated** — the file alone, as captured.
|
|
64
|
+
- **hostile** — a different timezone, locale and RSpec seed.
|
|
65
|
+
- **neighbored** — the file twice in one process, so accumulated state shows up.
|
|
66
|
+
|
|
67
|
+
The emitted spec forces the capture's answer on every axis rather than inheriting
|
|
68
|
+
the suite's: isolation regime, queue adapter, clock, seed, locale and zone. A suite
|
|
69
|
+
that truncates instead of transacting, or that runs jobs inline, would otherwise
|
|
70
|
+
turn a green capture into a red or vacuous spec.
|
|
71
|
+
|
|
72
|
+
## No database ids in a pin
|
|
73
|
+
|
|
74
|
+
Postgres sequences are not transactional, so a rolled-back case still advances them
|
|
75
|
+
and an id differs on the next run. A foreign key pointing at a record the plan built
|
|
76
|
+
becomes a ref; anything else id-shaped becomes a wildcard:
|
|
77
|
+
|
|
78
|
+
```ruby
|
|
79
|
+
{"t" => "record", "class" => "Invoice", "attributes" => {
|
|
80
|
+
"customer_id" => {"t" => "ref", "v" => "customer_1"},
|
|
81
|
+
"total" => {"t" => "decimal", "v" => "110.0"}
|
|
82
|
+
}}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Ids also hide in job arguments (`perform_later(record.id)`) and inside GlobalID
|
|
86
|
+
strings (`gid://app/Invoice/22`). Both are resolved to the record they name, or keep
|
|
87
|
+
the model and drop the id.
|
|
88
|
+
|
|
89
|
+
## Real rows, with the personal data rewritten
|
|
90
|
+
|
|
91
|
+
`--sample` reads rows from your development database through a generated read-only
|
|
92
|
+
script, then rebuilds them as `create!` calls in the spec. Rewrites preserve **domain
|
|
93
|
+
and length** — `rehan.munir@acme.co` keeps `@acme.co` and its character count — so a
|
|
94
|
+
target that routes on a domain or validates a length behaves as it always did. Row
|
|
95
|
+
sources are hashed, so a committed spec does not map back to production rows.
|
|
96
|
+
|
|
97
|
+
`--no-redact` turns this off. It writes real personal data into a file you commit.
|
|
98
|
+
|
|
99
|
+
## It refuses rather than guessing
|
|
100
|
+
|
|
101
|
+
| Situation | Result | Exit |
|
|
102
|
+
| --- | --- | --- |
|
|
103
|
+
| target takes a block or yields | `BlockRequired` | 4 |
|
|
104
|
+
| constructor resolves its own dependencies, or `super`s into another file | `UnresolvableSetup(:opaque_constructor)` | 5 |
|
|
105
|
+
| a parameter names a model the app has no table, model or factory for | `UnresolvableSetup(:unresolvable_parameter)` | 5 |
|
|
106
|
+
| two tables require each other through NOT NULL foreign keys | `UnresolvableSetup(:association_cycle)` | 5 |
|
|
107
|
+
| a NOT NULL column whose type has no honest value | `UnresolvableSetup(:unknown_column_type)` | 5 |
|
|
108
|
+
| target reads an attachment | `UnresolvableSetup(:attachment)` | 5 |
|
|
109
|
+
| `ros-apartment` tenancy | `UnresolvableSetup(:apartment)` | 5 |
|
|
110
|
+
| name resolves to two definitions | `AmbiguousTarget` | 3 |
|
|
111
|
+
| `delegate` or `method_missing` redirect | `TargetNotFound` | 2 |
|
|
112
|
+
| `db/structure.sql` instead of `db/schema.rb` | `SchemaFormatUnsupported` | 6 |
|
|
113
|
+
| Rails below 6.0 | `UnsupportedRailsVersion` | 10 |
|
|
114
|
+
| no case was stable across boots | `NothingStableToPin` | 8 |
|
|
115
|
+
|
|
116
|
+
It will not pass `nil` for a model it could not build: the target would raise on nil,
|
|
117
|
+
and that error would be pinned as though your application produced it.
|
|
118
|
+
|
|
119
|
+
## Mutation scoring
|
|
120
|
+
|
|
121
|
+
`pinspec validate` grades each aspect of a pin separately, because they are blind to
|
|
122
|
+
different things — a return-value pin does not notice a deleted `perform_later`, and
|
|
123
|
+
a job pin does not notice the arithmetic:
|
|
124
|
+
|
|
125
|
+
```
|
|
126
|
+
return 66.7% weak (4 killed, 2 survived)
|
|
127
|
+
survived: statement_removal at line 18 - SyncJob.perform_later(invoice.id)
|
|
128
|
+
jobs 50.0% weak (3 killed, 3 survived)
|
|
129
|
+
survived: arithmetic at line 14 - +
|
|
130
|
+
|
|
131
|
+
nothing survived every aspect: together, the pins cover this target.
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
A pin containing a wildcard or a truncated value is never reported as strong, however
|
|
135
|
+
well it scores. An aspect the pin does not assert is skipped rather than scored, since
|
|
136
|
+
scoring an absent aspect yields a vacuous 100%.
|
|
137
|
+
|
|
138
|
+
## Known limits
|
|
139
|
+
|
|
140
|
+
- **Attachments** are refused rather than run against an empty blob.
|
|
141
|
+
- **`after_commit`** never fires under transactional isolation, in the capture or in
|
|
142
|
+
the emitted spec. pinspec does not fake it, so this is a real divergence from
|
|
143
|
+
production and the report says so.
|
|
144
|
+
- **Multiple writing databases**: per-case rollback covers the primary writing
|
|
145
|
+
connection only.
|
|
146
|
+
- **Read and clock detection scans the target's own file.** A transitive callee that
|
|
147
|
+
reads the current user or the process clock is invisible, so no warning is not proof
|
|
148
|
+
of no read.
|
|
149
|
+
- **Small worlds make weak pins.** The default corpus builds the smallest world that
|
|
150
|
+
can exist, which often does not reach a target's branches — mutation scores on real
|
|
151
|
+
service objects are frequently weak. `--sample` helps; read the scores before
|
|
152
|
+
trusting a pin.
|
|
153
|
+
|
|
154
|
+
## Development
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
bundle install
|
|
158
|
+
bundle exec rspec
|
|
159
|
+
LANG=C LC_ALL=C TZ=Etc/GMT+8 bundle exec rspec
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
That second run matters: pinspec's own hostile verify config sets `LANG=C`, so a
|
|
163
|
+
non-ASCII string literal in shipped source would break it.
|
|
164
|
+
|
|
165
|
+
Fixtures come in two kinds. Those under `spec/fixtures/targets/` and most of
|
|
166
|
+
`spec/fixtures/apps/` are parsed, never loaded, so they reference constants that do
|
|
167
|
+
not exist in this repo — that is the point, since the analyzer must work against a
|
|
168
|
+
repo you have not booted. Two of them are real Rails apps that boot on PostgreSQL and
|
|
169
|
+
cover both isolation regimes; the specs needing them skip rather than fail when they
|
|
170
|
+
are not prepared:
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
cd spec/fixtures/apps/rails71_basic && bundle install && RAILS_ENV=test bundle exec rails db:schema:load
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
`spec/equivalence/host_equivalence_spec.rb` is the one to read first. It holds the
|
|
177
|
+
probe process and the spec process to the same answer across all six axes they could
|
|
178
|
+
disagree on, and each fixture is configured to disagree with the plan so that
|
|
179
|
+
agreement cannot happen by accident.
|
|
180
|
+
|
|
181
|
+
## License
|
|
182
|
+
|
|
183
|
+
MIT
|