A test fake is scoped to its subject, not to an API
The rule. A stand-in store, client or transport fails the OPERATION the test is named for β never the whole method it happens to arrive through. Every other caller of that method is a collaborator the test is not about, and the day core starts asking the same method a different question, the test reds for a reason nobody wrote down.
What it cost (measured, 2026-09-17)
MeshWeaver.Plugins main was dark from 14:38Z. Two roots, both satellite halves owed by core
changes β the #2689 dependent-suite gap:
core merges, its own CI is green, and the first thing that sees the break is a satellite's suite
hours later. This page is about the second one.
BulkSaveInstallTest.WhenTheBulkReadFails_EveryNodeFallsBackToTheRequestPath pins a real
resilience property: when the install's ONE bulk existence read fails, bulk routing is disabled
entirely and every node takes the validating request path. Its stand-in armed that with a flag:
public IObservable<MeshNode> ReadMany(IReadOnlyCollection<string> paths, JsonSerializerOptions o)
=> FailReadMany
? Observable.Throw<MeshNode>(new InvalidOperationException("simulated bulk-read failure"))
: ((IStorageAdapter)_inner).ReadMany(paths, o);
ReadMany is not "the installer's bulk read". It is the store's read-by-path API, and a
single-path ReadMany is how a collaborator asks a point question without the repair proxy a plain
Read carries. So the flag did not simulate a failed bulk read β it simulated a store with no
read-by-path at all. Measured in one local run, it was taking down three things the test is not
about:
| collaborator | what the blanket failure did |
|---|---|
StorageAdapterMeshQueryProvider.ExactScope |
every exact-scope query threw |
InstalledPackageRepairService's completeness sweep |
reported NOT VERIFIED (NotObserved) |
PartitionOwningTypes.OwnsPartition (core #4449) |
its ONE-path read answered "cannot tell", so every create was refused |
The third is what reddened main:
Validator rejected node creation at BulkPack/Deep: Whether 'BulkPack/Thing' owns its partition
could not be established β its NodeType definition could not be read within 10 s.
π¨ That refusal is correct. Core resolves partition ownership by reading the NodeType's durable
row, and an ownership question that cannot be answered fails closed and is retryable β not a
verdict (access.partitionCreate.undetermined). It reads through ReadMany deliberately, because
a plain Read of a partition-root-shaped path goes through LegacyUserPartitionRepair, which may
WRITE a repaired root β and an ownership CHECK may not write anything. Nothing here is core's bug.
The fake was simply wider than its own sentence.
The fix is one predicate β fail a bulk read, which is what the test is named for β and the flag takes the operation's name rather than the method's:
public bool FailBulkRead { get; set; } // was FailReadMany
β¦
=> FailBulkRead && paths.Count > 1 ? β¦ : β¦
The installer's seven-path existence read still fails, so the property under test is unchanged; a collaborator's one-path question still gets an answer.
How to tell whether your fake is too wide
Before arming a failure in a stand-in, ask who else calls this method on a live mesh β not who calls it in this test. If the answer is "anything that reads a node by path", the flag is a statement about the whole store and the test is silently asserting far more than its name.
- Name the operation, not the method.
FailBulkReadis a subject;FailReadManyis an API. - Scope by the shape the subject has β a batch size, a path prefix, a call ordinal β so the first collaborator to arrive is not collateral damage.
- A test whose fake disables an API is a test that will red on somebody else's change. That is not a flake and it is not their bug; it is this test asserting something it never meant.
The other half of that day, for the record
The first root was the same class one level up: core 4e7e9fd182 removed @@("area/Search") from
SpaceNodeType.WelcomeMarkdown ("the welcome page stops restating the index"), and the gate that
appends the default bottom catalog lives HERE β so the default body was read as "no catalog authored
yet" and given one at the bottom, the restatement core had just removed from the top. Fixed in
SpaceLayoutAreas.TakesDefaultBottomCatalog.
Both halves were invisible to core's own CI by construction. The compensating control is this
repository's daily run and its PR lanes β which is why a dark main is urgent rather than tidy: while
it is red, every pull request here resolves a platform set main has passed on, and once that set
ages out of the runner volume nothing in the repository can run its gate at all. That deadlock, and
the platform:newest opt-out that breaks it, is in
Platform resolution.
Related
- Build and release process β when a node repo builds against which platform set.
- Platform resolution β how a run picks its set, and the ceiling a pull request follows.