Skip to content

Phase 5: Create Farm.Infrastructure.Tests - #2084

Merged
jpapiez merged 22 commits into
developmentfrom
dev/jpapiez/farm-infrastructure-tests
Aug 27, 2026
Merged

Phase 5: Create Farm.Infrastructure.Tests#2084
jpapiez merged 22 commits into
developmentfrom
dev/jpapiez/farm-infrastructure-tests

Conversation

@jpapiez

@jpapiez jpapiez commented Aug 26, 2026

Copy link
Copy Markdown
Collaborator

Summary

Splits cohort C (services with no dependency on Farm.Web.Api.* or a web host — Notifications, Printers, Queue, ShiftPlan, Attention, AutoDispatch, Statistics, Idempotency, Cameras, FailureDetection, Sync, Cost, RateLimiting, Repositories, Domain, Data, DataManagement, Locations, Discovery, Dtos, Builders, Logging, Network, Migrations, most of Dispatch and Infrastructure) out of Farm.Web.Api.Tests into a new Farm.Infrastructure.Tests project, per Phase 5 of epic #2019 (decomposing Farm.Web.Api into vertical-slice module assemblies with a matching test-project split).

What changed

  • New src/tests/Farm.Infrastructure.Tests/Farm.Infrastructure.Tests.csproj, referencing Farm.Infrastructure + backend plugins + migration projects — not Farm.Web.Api.
  • Added to src/farm-web.sln.
  • InternalsVisibleTo("Farm.Infrastructure.Tests") added to src/infra/Properties/AssemblyInfo.TestsVisible.cs in the first commit of the branch, before any file move.
  • Domain groupings (Notifications, Dispatch, Printers, Slicing, Filament, Admin) declared as shards[] in the CI test manifest — a runtime partition for parallel wall-clock execution, not a compile-time blast-radius split. Shards are exhaustive, mutually exclusive, and non-empty (enforced by scripts/ci/tests/test-dotnet-test-manifest.sh).
  • scripts/ci/tests/test-select-dotnet-tests.sh extended with positive/negative/mixed-path/full-safe cases for src/infra/**, so a change touching only src/infra/** selects the Farm.Infrastructure.Tests leg without building Farm.Web.Api.
  • scripts/ci/generate-codeql-slnf.sh updated to include the new production/test project pairing appropriately.
  • scripts/ci/tests/test-dotnet-test-manifest.sh and its regression suite test-dotnet-test-manifest-checks.sh hardened across several review rounds (see commit history) for correctly parsing C# 11 raw-string-interpolation syntax when detecting test classes, so the shard-coverage validator does not misattribute classes inside multi-dollar ($$""") raw string literals.
  • Zero product-code diff: git diff origin/development HEAD --diff-filter=M -- src/api src/infra shows only AssemblyInfo.TestsVisible.cs changed.
  • No EF Core migrations — no entity types moved; all entities and AppDbContext remain in Farm.Infrastructure.
  • Total test count across all assemblies unchanged (files moved, not duplicated or dropped).

Validation

  • dotnet build ./farm-web.sln — 0 errors (57 pre-existing warnings, none new).
  • dotnet test on Farm.Infrastructure.Tests.csproj (via solution filter): 1933 passed / 6 failed — the 6 failures are pre-existing environment-only skips requiring live PFARM_TEST_SQLSERVER_CONN/PFARM_TEST_POSTGRES_CONN connection strings not available in this sandbox.
  • dotnet test on Farm.Web.Api.Tests.csproj directly: 4062/4062 passed, confirming the cohort-C move did not drop coverage.
  • dotnet format ./farm-web.sln --verify-no-changes — clean, aside from pre-existing CHARSET warnings on 5 files this branch never touches (confirmed via git diff origin/development — zero diff on those paths).
  • bash scripts/ci/tests/test-select-dotnet-tests.sh — 113/113 passed, including the new src/infra/** selection cases.
  • bash scripts/ci/tests/test-dotnet-test-manifest-checks.sh — 15/15 passed.
  • bash scripts/ci/tests/test-dotnet-test-manifest.sh (real-codebase validator) — PASS clean.

Review

Reviewed by Bishop (claude-opus-5), Hicks (gpt-5.6-sol), and Vasquez (gemini-3.1-pro-preview) — unanimous APPROVE at head a789e297ad7fca97f05f38fb5aea96751be6bd9e. Verdict comments posted below in canonical format.

Closes #2033

Copilot AI added 16 commits August 26, 2026 11:56
Register the new Farm.Infrastructure.Tests.csproj in farm-web.sln and
grant it InternalsVisibleTo access from Farm.Infrastructure, before any
cohort-C test file is moved into it. Isolating this from the bulk move
keeps the Parker gate diff on src/infra clean (issue #2033, epic #2019
phase 5).

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Move the 268 test files (Services/Notifications, Services/Printers,
Services/Queue, Services/ShiftPlan, Services/Attention,
Services/AutoDispatch, Services/Statistics, Services/Idempotency,
Services/Cameras, Services/FailureDetection, Services/Sync,
Services/Cost, Services/RateLimiting, Repositories, Domain, Data,
DataManagement, Locations, Discovery, Dtos, Builders, Logging,
Network, Migrations, and most of Dispatch/Infrastructure) that
reference neither Farm.Web.Api.* nor a web host out of
Farm.Web.Api.Tests into Farm.Infrastructure.Tests, per the epic #2019
phase 5 cohort-C split.

Also relocates the shared TestHelpers/ProviderDatabaseTestCollection
into Farm.Testing.Shared as AppDbTestHelpers so both test projects can
consume it without a cross-test-project reference, and moves the
sample_gcode fixture directory alongside the tests that read it.

No entity types, EF Core migrations, or product code moved — only test
sources and test fixtures. Total test count is unchanged; coverage was
verified project-by-project before this commit.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Extend select-dotnet-tests.sh with a new tests_infra classify_path
case and has_tests_infra bucket, and split the previously-combined
has_api/has_infra test-selection block: has_infra now selects
Farm.Infrastructure.Tests, Farm.Slicer.Module.Tests (which has a
direct production ProjectReference to Farm.Infrastructure),
Farm.OrcaSlicer.Worker.Tests, and Farm.Modules.SmartPlug.Tests, while
deliberately excluding Farm.Web.Api.Tests/Farm.Web.IntegrationTests so
an src/infra-only change no longer selects or builds Farm.Web.Api's
test legs. The separate dotnet-build job still compiles Farm.Web.Api
for infra changes, so compile coverage is unaffected.

Farm.Infrastructure.Tests is also added to the has_backend,
has_backend_core, has_slicer, has_mig_app, and has_mig_slcr buckets,
since the new project's ProjectReference graph spans backend plugins,
the slicer module, and both migration project families.

Register Farm.Infrastructure.Tests in dotnet-test-manifest.json with
its full dependsOnProjects list and 6 domain shards (notifications,
dispatch, printers, slicing, filament, admin) — a runtime partition
for parallel wall-clock only, verified exhaustive, mutually exclusive,
and non-empty across all 26 moved namespace groups.

Extend test-select-dotnet-tests.sh with 9 new cases covering the
narrow infra bucket, the infra+api mixed case, full-safe inclusion,
and the has_backend/has_slicer/has_mig_* additions; all 99 cases pass.

generate-codeql-slnf.sh needs no change: it already auto-excludes
anything under */tests/* and auto-includes Farm.Infrastructure.csproj.

docs/CI.md's bucket table is updated to match.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…rm-infrastructure-tests

# Conflicts:
#	docs/CI.md
#	scripts/ci/dotnet-test-manifest.json
#	scripts/ci/select-dotnet-tests.sh
#	scripts/ci/tests/test-select-dotnet-tests.sh
#	src/farm-web.sln
…ds (#2033)

- Drop Farm.Web.Api.Tests namespacePrefixes for directories the cohort-C
  move emptied (Builders, Discovery, Domain, Dtos, Logging, Migrations,
  Network, Repositories) and remove the now-stale empty directories on
  disk. Git does not track empty directories, so on a fresh checkout
  these prefixes pointed at nonexistent namespaces and made
  test-dotnet-test-manifest.sh fail closed -- it only passed locally
  because the leftover empty dirs from git mv were still present in
  this worktree.
- Rename IAppSettingTests.cs's outlier namespace
  (Farm.Infrastructure.Settings.Tests -> Farm.Infrastructure.Tests.Infrastructure)
  so it is covered by the 'slicing' shard like its sibling files in the
  same directory, instead of silently matching zero shards.
- Add trailing dots to every Farm.Infrastructure.Tests shard filter term,
  matching the established Farm.Web.Api.Tests convention, fixing a real
  overlap where the 'filament' shard's bare 'Data' term substring-matched
  the 'admin' shard's 'DataManagement' term (all 6 DataManagement test
  files were running in both shards).
- Generalize test-dotnet-test-manifest.sh's shard-exhaustiveness /
  mutual-exclusivity / non-empty validation from a Farm.Web.Api.Tests-only
  check to loop over every manifest entry with shards. It now also proves
  Farm.Infrastructure.Tests' shards via a per-source-file filter match
  (needed because its shards use nested Parent/Child namespacePrefixes,
  e.g. Services/Notifications, that split a single top-level directory
  across multiple shards -- directory-level enumeration alone cannot
  prove exhaustiveness there).
- Restore the Farm.Web.Api.Tests 'infra' shard's Locations prefix: one
  file (LocationHierarchyTests.cs) legitimately stayed behind because it
  references Farm.Web.Api.Tests.TestInfrastructure / a web host, so
  Locations is correctly split between both projects, same as Data/
  DataManagement.

Found by Bishop (claude-opus-5) and Hicks (gpt-5.6-sol) in the mandatory
pre-PR adversarial review.
#2033)

Hicks' round-2 review found that the manifest validator's per-file
shard-coverage check derived its FullyQualifiedName candidate from the
file name rather than the actual class declaration, so a class rename
independent of its file name could pass validation while VSTest's
real class-name-based filter selected nothing for it.

Rather than the filename-identity fix originally attempted (too
strict -- several existing files legitimately declare more than one
test class per file), attribute each [Fact]/[Theory] attribute to the
nearest preceding *public* class declaration in the same file. This
mirrors xUnit's actual discovery constraint (only public types are
reflectively discoverable) and correctly excludes private/internal
nested helper classes (e.g. a local IHttpClientFactory stub) from
being mistaken for the owning test class of a fact declared after
them.

Re-ran scripts/ci/tests/test-dotnet-test-manifest.sh (PASS) and
scripts/ci/tests/test-select-dotnet-tests.sh (107/107) on the fixed
validator; re-verified the Parker gate is unaffected.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 2d4a52db-9098-4ded-a2cd-1b34adac0e65
…r attribution (#2033)

Hicks (round 3 review) found that the 'nearest preceding public class'
heuristic still mis-attributes facts declared in an outer test class to a
nested public IClassFixture factory (e.g. 'public class Factory :
CustomWebApplicationFactory' nested inside a test class), because the
nested class's own body has already closed in linear text order by the
time a later fact appears. 18 files / ~159 attributes in
Farm.Web.Api.Tests use this common xUnit fixture idiom.

True lexical containment requires brace matching, which is unreliable via
regex given C# string interpolation; instead this uses each class
declaration's line indentation as a nesting-depth proxy (verified
empirically consistent across the codebase): only classes at a file's
shallowest class-declaration indentation are treated as candidate 'owning'
classes for attribute attribution, so nested fixtures are excluded
entirely.

Re-verified: manifest validator PASS, test-select-dotnet-tests.sh 107/107,
HealthCheckDiscoveryTests.cs (nested private class) and
RequestObjectsTests.cs (4 sibling top-level classes) still attribute
correctly.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 2d4a52db-9098-4ded-a2cd-1b34adac0e65
…rd validator (#2033)

Hicks (round 4 review) pointed out that the indentation-based nested-class
filter would silently misattribute facts if a genuine sibling top-level
class were accidentally indented differently from its neighbor (e.g. by a
stray extra space) -- the file's minimum-indentation class would 'win' and
the sibling's facts would vanish from shard-coverage consideration instead
of surfacing a validator error. Farm.Web.Api.Tests' root shard uses
class-specific FullyQualifiedName filters today, so this is not merely a
future/theoretical risk.

Add an explicit guard: every public class in a file must sit at either the
file's minimum indentation (top-level) or exactly one 4-space nesting step
above it (the one nested-fixture idiom this heuristic exists to handle).
Any other indentation pattern -- more than two distinct levels, or a gap
that isn't a clean 4-space step -- fails the validator closed with an
itemized error instead of guessing.

Also fixes a pre-existing, previously undetected bug in this session's own
work: scripts/ci/tests/test-dotnet-test-manifest-checks.sh's
case_api_shard_filter_coverage_fails asserted a stale error-message
substring ('filter does not cover test source ...') that no longer matches
the validator's current wording ('no shard filter covers test source ...
class ...'), introduced by an earlier commit this session
(5cf3c87). That assertion was silently failing every run because this
regression suite for the validator itself was never re-run this session
until now -- caught while adding the new indentation-mismatch regression
case below it. Updated the assertion to match current wording and added
case_sibling_indentation_mismatch_fails, which writes a small scratch .cs
fixture with two top-level classes at mismatched indentation directly
into the real Farm.Web.Api.Tests/Controllers/ tree, confirms the
validator now fails closed on it, and removes the fixture afterward.

Re-verified: test-dotnet-test-manifest.sh PASS, its own regression suite
(test-dotnet-test-manifest-checks.sh) 10/10 (was silently 8/9 passing +
1 pre-existing broken assertion), test-select-dotnet-tests.sh 107/107,
Parker gate clean (only AssemblyInfo.TestsVisible.cs differs under
src/api or src/infra), working tree clean.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 2d4a52db-9098-4ded-a2cd-1b34adac0e65
…n shard validator (#2033)

Round 5 review (Hicks) found a residual false-pass window in the
indentation-based fail-closed guard: a genuine top-level sibling class
accidentally indented by exactly one 4-space nesting step is still
silently misattributed as a nested fixture, since indentation alone
never actually proves ownership.

Replace the indentation heuristic entirely with real C# brace-depth
analysis:
- _strip_noncode() blanks out comments and string/char literal content
  (including nested interpolation holes, e.g. the ToString-in-hole
  case) so brace counting only sees real code braces.
- _code_brace_depths() does a simple forward brace-depth scan over the
  stripped text.
- A class is now recognized as top-level iff its brace depth equals the
  file's minimum class depth, and that minimum must be exactly 0
  (namespace scope, since this codebase uses file-scoped namespaces
  exclusively) or the validator fails closed with an explicit error
  rather than guessing.

This is correct regardless of formatting/indentation, closing the
entire class of misattribution bugs found across rounds 3-5.

Regression suite updates:
- Replace case_sibling_indentation_mismatch_fails (which tested a
  scenario that is no longer a bug) with
  case_sibling_mismatched_indentation_both_flagged_uncovered_fails,
  proving mismatched indentation no longer causes a sibling class to be
  silently excluded from shard-coverage consideration.
- Add case_block_scoped_namespace_fails_closed, exercising the new
  min_depth != 0 fail-closed guard via a block-scoped namespace fixture.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 2d4a52db-9098-4ded-a2cd-1b34adac0e65
…rm-infrastructure-tests

# Conflicts:
#	docs/CI.md
#	scripts/ci/dotnet-test-manifest.json
#	scripts/ci/select-dotnet-tests.sh
#	src/farm-web.sln
…ln (#2033)

Round-6 adversarial review (Bishop/Hicks/Vasquez) found three issues in the
prior commits:

1. src/farm-web.sln had a stray, orphaned `EndProject` line left over from
   the merge-conflict resolution against origin/development, immediately
   after the `Farm.Modules.PrintQueue.Tests` project block. Project/
   EndProject counts were unbalanced (62/63); this corrupts the solution
   file for any tool that parses it strictly. Removed the duplicate line;
   counts are now balanced (62/62) and `dotnet build ./farm-web.sln`
   succeeds.

2. scripts/ci/tests/test-dotnet-test-manifest.sh's `_strip_noncode` had no
   handling for C# 11 raw string literals (`"""..."""`, optionally
   interpolated via a `$` prefix). A `"""` was parsed as an empty string
   (`""`) followed by a new string opening (`"`), which could desync the
   code/non-code partition for the remainder of the file -- e.g. a `"` or
   `//`-looking substring inside the raw string's body could terminate a
   (mis-identified) string early or start a spurious line comment, silently
   blanking real code including a class's closing brace. This is exactly
   the class of false-pass bug rounds 3-6 have been closing: it could hide
   a top-level class or misattribute its [Fact]/[Theory] tests to the wrong
   owner. Reviewers reproduced a concrete failure against
   PerToolAttributionDtoSerializationTests.cs (one of 13 files already
   using raw strings in the very project this phase adds).

   Added `scan_raw_string`: recognizes an opening run of >=3 `"` characters
   (with an optional leading run of `$` for interpolation) and blanks the
   entire literal through to the first closing run of at least as many
   quotes, found strictly after the opening delimiter. Interpolation holes
   inside a raw string are blanked wholesale rather than hole-scanned
   (blunter than the existing ordinary/verbatim-interpolated handling, but
   safe: a hole's braces never contribute to the code-side brace count
   either way).

3. Added a fail-closed backstop: after computing a file's code-brace
   depths, verify the depth returns to exactly 0 at EOF before trusting any
   class's depth. If it doesn't -- because of this or any future unhandled
   C# construct -- the validator now reports an explicit "unbalanced
   braces after comment/string stripping" error and refuses to guess class
   ownership, rather than silently using a desynced depth. This converts
   the entire remaining class of tokenizer gaps from a silent false pass
   into a loud, actionable failure.

Added two regression cases to test-dotnet-test-manifest-checks.sh:
- case_raw_string_literal_does_not_desync_brace_depth: reproduces the
  reviewers' exact failure mode (raw string containing an embedded `"` and
  a `//`-looking substring) and proves both the raw-string-containing class
  and its sibling are still correctly recognized as separate top-level
  classes.
- case_unbalanced_braces_fails_closed: a stray unmatched brace anywhere in
  the file must trip the new EOF-balance guard.

Regression suite: 13/13 pass. Real-codebase run
(test-dotnet-test-manifest.sh against the checked-in manifest): PASS.
test-select-dotnet-tests.sh: 110/110 pass (unaffected). Parker gate holds
(only src/infra/Properties/AssemblyInfo.TestsVisible.cs differs from
origin/development under src/api or src/infra). dotnet build
./farm-web.sln: 0 errors after the sln fix.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 2d4a52db-9098-4ded-a2cd-1b34adac0e65
…ted raw string in hole

Round-7 review (Hicks) found that scan_raw_string blanks interpolation
hole contents wholesale rather than hole-scanning them, so a raw string
literal nested inside another raw string's own interpolation hole, with
an opening quote run >= the outer literal's own, is mistaken for the
outer literal's closing delimiter. This is a real tokenizer gap.

Empirically verified this session:
- No raw string literal anywhere under src/ actually nests another raw
  string inside an interpolation hole (35 files use interpolated raw
  strings; every hole interpolates only plain identifiers/format
  specifiers).
- The adversarial construction desyncs brace depth as predicted, but
  leaves the file's overall code-brace depth nonzero at EOF, which the
  round-6 EOF-balance guard (case_unbalanced_braces_fails_closed) already
  fails closed on with an explicit "unbalanced braces" error -- not a
  silent misattribution.

Changes:
- scan_raw_string's docstring now honestly documents this residual,
  out-of-scope limitation instead of overclaiming unconditional safety.
- Added case_nested_raw_string_in_hole_fails_closed to the manifest
  validator regression suite, proving the EOF-balance guard fires for
  this exact construct.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 2d4a52db-9098-4ded-a2cd-1b34adac0e65
…rm-infrastructure-tests

# Conflicts:
#	docs/CI.md
#	scripts/ci/select-dotnet-tests.sh
#	scripts/ci/tests/test-select-dotnet-tests.sh
…-8 Hicks finding)

Round-7 Hicks finding: scan_raw_string blanked an interpolation hole's
contents wholesale, so a nested raw string inside the hole with an opening
quote run >= the outer's could be mistaken for the outer's own closer.
Round-6/7 mitigation was an EOF-balance guard plus a docstring disclosure.

Round-8 Hicks finding: an EQUAL-length nested quote run (3-vs-3) is more
dangerous than the longer case, because the hole's open and close braces
both get swallowed symmetrically, so the file's overall brace count can
stay balanced at EOF -- the EOF-balance guard alone cannot catch it.
Empirically confirmed via a standalone extraction of the embedded Python:
the 3-vs-3 construction desyncs the scan but happens to leave code_depths
at 0, a silent false pass.

Fix: scan_raw_string now takes the interpolated literal's dollar_count and,
for dollar_count >= 1, treats a lone '{' in body text as an interpolation
hole -- blanking the brace and recursing into scan_code(...,
stop_at_hole_close=True), the same hole scanner scan_string already uses
for ordinary interpolated strings -- instead of naively searching body text
for the next quote_run-or-more run. A nested string literal inside the hole
is now scanned by its own dedicated, independent closing-delimiter search,
so it can never be mistaken for the outer literal's own closer, regardless
of whether its own quote run is shorter, equal to, or longer than the
outer's. Doubled '{{'/'}}' outside a hole are treated as literal braces,
mirroring scan_string's existing escape handling. dollar_count == 0
(non-interpolated raw strings, which have no holes) keeps the prior blunt
whole-body blank, which is unconditionally safe since no hole exists.

Replaces case_nested_raw_string_in_hole_fails_closed (a negative test
documenting the gap as an accepted, backstopped limitation) with
case_nested_raw_string_in_hole_does_not_desync_brace_depth (a positive
test proving both the round-7 longer-quote-run and round-8 equal-quote-run
constructions are now parsed correctly, with all three classes in the
fixture correctly and separately attributed as coverage gaps and no
"unbalanced braces" error at all).

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 2d4a52db-9098-4ded-a2cd-1b34adac0e65
…und-9 Hicks finding)

The round-8 fix modeled an interpolated raw string's hole-opening as
"a single '{' opens a hole; a doubled '{{' is an escaped literal brace",
borrowing ordinary interpolated-string escaping semantics (`scan_string`'s
`{{` == literal `{`). That escaping rule does not exist in raw string
interpolation at all: C# 11 instead requires a run of exactly
`dollar_count` consecutive '{' to open a hole (and a matching run of
`dollar_count` consecutive '}' to close it), for any dollar_count >= 1.
For dollar_count == 1 the round-8 rule happened to coincide with the
correct one, but for dollar_count >= 2 it is backwards: a lone '{' is
literal text (too few to open a hole), while a genuine '{{' run is the
real hole-opener the round-8 code mistook for an escaped literal.

This is a live gap, not a theoretical one: 37 files in this repository
use `$$"""` two-dollar raw string literals today (Farm.Infrastructure.Tests,
Farm.Moonraker.Emulator.Tests, Farm.OrcaSlicer.Worker.Tests,
Farm.Slicer.Module.Tests, Farm.Web.Api.Tests, Farm.Web.IntegrationTests,
and src/api/Services/Startup/MoonrakerEmulatorSeeder.cs), including
GcodeCommandTests.cs's `$$"""{"scenario":"{{scenario}}"}"""`, where the
single braces around the literal JSON text are NOT hole-openers and the
doubled `{{scenario}}` is the genuine hole.

Rewrite scan_raw_string's hole detection to measure the run-length of
consecutive '{' at the current position: if the run is >= dollar_count,
consume exactly dollar_count characters as the hole-opener and recurse
into scan_code(..., stop_at_hole_close=True, hole_close_run=dollar_count),
letting any excess '{' beyond dollar_count flow into the hole's own code
scan (correct, since they are genuine code once the hole has opened);
if the run is shorter than dollar_count, it is literal text and is
skipped over untouched. Thread a new hole_close_run parameter (default 1,
preserving prior behavior for ordinary interpolated strings and the
dollar_count == 1 raw-string case) through scan_code's stop_at_hole_close
handling so a hole only closes, at brace-nesting depth zero, on a
matching run of at least hole_close_run consecutive '}'. Drop the now
superseded "doubled brace is an escaped literal" special case entirely --
it was borrowed from the wrong string-literal escaping model and is
replaced by the single run-length-vs-dollar_count comparison above, which
is correct uniformly for dollar_count == 1 and dollar_count >= 2 alike.

Also fix the scan_raw_string docstring itself: it previously embedded a
literal `$$"""..."""`-shaped example directly inside its own Python
triple-quoted docstring, which prematurely terminated the docstring at
parse time -- the same class of bug this function exists to guard
against. Reworded to describe the construction in prose instead of
embedding it literally.

Empirically re-verified (via standalone extraction of the embedded Python
into a scratch script and direct invocation of _strip_noncode /
_code_brace_depths, then deleted) against: the round-7 4-vs-3 nested
raw-string-in-hole case, the round-8 3-vs-3 equal-quote-run case, Hicks'
round-9 two-dollar construction
`$$"""outer{{Build("""nested""")}}outer"""`, the real GcodeCommandTests.cs
`$$"""{"scenario":"{{scenario}}"}"""` shape, a plain single-dollar
object-initializer hole, a non-interpolated raw string, and a
triple-dollar (dollar_count == 3) hole -- all parse to a balanced EOF
brace depth with the hole's contents correctly excluded from the file's
top-level class/brace structure.

The existing case_nested_raw_string_in_hole_does_not_desync_brace_depth
test is unchanged (still correct and still exercised); added a new
regression case, case_multi_dollar_raw_string_hole_requires_matching_brace_run,
proving a
two-dollar raw string's hole (opened/closed by a genuine '{{'/'}}' run,
with literal single braces around ordinary text) is now handled correctly
and the class following it is still separately and correctly reported as
a coverage gap. Updated the header-comment item list with a new item #15
describing this fix. Full regression suite: 15/15 PASS. Real-codebase
validator: PASS, clean (all 37 real `$$"""` files parse correctly under
the new logic). Parker gate unchanged: only
src/infra/Properties/AssemblyInfo.TestsVisible.cs differs from
origin/development. No .cs files touched by this fix; only the two CI
validator .sh scripts.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 2d4a52db-9098-4ded-a2cd-1b34adac0e65
… findings)

Hicks' round-10 review found that the round-9 fix (30dc801), while
correctly requiring a full dollar_count-length brace run to open/close a
hole in a multi-dollar raw string, got the attribution of EXCESS braces
backwards when an opening run is longer than dollar_count. Per the C# 11
raw-string-interpolation spec (dotnet/csharplang proposal, "Detailed
design (interpolation case)"), for an opening run of length L >=
dollar_count N, the LAST N braces of the run open the hole and any excess
(L - N) braces at the START of the run are literal content -- the round-9
fix instead consumed the FIRST N braces as the opener, which is backwards.
The closing side was already correct as originally written (it consumes
the FIRST N braces of a closing run and leaves excess trailing braces as
literal content), which happens to match the spec's rule for closing.

This was not cosmetic: consuming the wrong end of an opening run desyncs
scan_code's hole_depth bookkeeping whenever the matching closing run has
no excess of its own to absorb the discrepancy, causing the scanner to run
past the raw string literal's true closing delimiter and swallow
everything after it -- confirmed empirically by extracting both the
round-9 and this corrected tokenizer as standalone scripts and running
them against `$$"""literal{{{Build("""nested""")}}outer"""` (open run of
3 for dollar_count=2, one excess brace; close run of exactly 2, no
excess): the round-9 code produced eof_depth=2 and silently swallowed a
following sibling class, while the corrected code produces eof_depth=0
with both classes intact.

Bishop's round-10 review separately found that the round-9 regression
test's fixture (`$$"""outer{{Build("""nested""")}}outer"""`) could not
actually discriminate old-vs-new behavior, because its open and close runs
are both exactly dollar_count long (no excess on either side) -- old and
new logic produce identical output on it. Replaced that fixture with the
excess-leading-brace construction above, which does discriminate (proven
via the same extraction-based empirical comparison).

- scripts/ci/tests/test-dotnet-test-manifest.sh: scan_raw_string's
  open-brace-run handling now blanks the LAST dollar_count characters of
  the run as the hole opener and resumes scan_code right after the full
  run, instead of the first dollar_count characters. Docstring rewritten
  to describe the corrected, spec-accurate excess-braces-pushed-to-the-
  outer-edge rule with a worked example. No change to the closing-side
  logic, which was already correct.
- scripts/ci/tests/test-dotnet-test-manifest-checks.sh: replaced
  case_multi_dollar_raw_string_hole_requires_matching_brace_run's fixture
  with the excess-leading-brace construction that actually discriminates
  old vs. new behavior; updated the header comment's item #15 to describe
  both the round-9 and round-10 findings.

Zero product-code diff (Parker gate unaffected: only
AssemblyInfo.TestsVisible.cs differs from origin/development). Full
regression suite: 15/15 PASS. Real-codebase validator: PASS clean.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 2d4a52db-9098-4ded-a2cd-1b34adac0e65
@github-actions github-actions Bot added the squad Squad triage inbox — Lead will assign to a member label Aug 26, 2026
@jpapiez

jpapiez commented Aug 26, 2026

Copy link
Copy Markdown
Collaborator Author

Squad-Reviewer: bishop
Squad-Verdict: APPROVE
Squad-Head-SHA: a789e29

@jpapiez

jpapiez commented Aug 26, 2026

Copy link
Copy Markdown
Collaborator Author

Squad-Reviewer: hicks
Squad-Verdict: APPROVE
Squad-Head-SHA: a789e29

@jpapiez

jpapiez commented Aug 26, 2026

Copy link
Copy Markdown
Collaborator Author

Squad-Reviewer: vasquez
Squad-Verdict: APPROVE
Squad-Head-SHA: a789e29

Copilot AI added 3 commits August 26, 2026 17:17
…rm-infrastructure-tests

# Conflicts:
#	docs/CI.md
#	scripts/ci/dotnet-test-manifest.json
#	scripts/ci/select-dotnet-tests.sh
#	scripts/ci/tests/test-select-dotnet-tests.sh
#	src/farm-web.sln
Bishop's review of the development-sync merge caught that
.github/workflows/ci.yml never gained an 'Upload Farm.Infrastructure.Tests
build' step, so every dotnet-test matrix leg for the new project failed
at the 'Download test build' step with 'Artifact not found'. All other
manifest-listed test projects have a matching upload step; this one was
missed when the project was first added.

Also fixes two merge-introduced stale comment counts in
scripts/ci/select-dotnet-tests.sh (seven modules, not eight; three
slicer-dependent modules, not four) and a stale claim in
scripts/ci/tests/test-select-dotnet-tests.sh about which legs 'reference
Farm.Web.Api.csproj' now that Farm.Modules.Gcode.Tests also does.
…rtial-reference legs

Bishop's second review pass (and independently Hicks) flagged that the
previous wording ('exercise the web host directly, e.g. via
CustomWebApplicationFactory') still wasn't a distinguishing property --
Farm.Slicer.Module.Tests and Farm.Modules.Gcode.Tests also reference
Farm.Web.Api.csproj and use CustomWebApplicationFactory, but only for a
subset of their test cases, and both are legitimately present in the
Farm.Infrastructure.Tests-adjacent matrix. Reworded to the precise
distinction: whole-suite vs. subset-of-cases web host targeting.
@jpapiez

jpapiez commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator Author

Squad-Reviewer: bishop
Squad-Verdict: APPROVE
Squad-Head-SHA: 24dec51

@jpapiez

jpapiez commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator Author

Squad-Reviewer: hicks
Squad-Verdict: APPROVE
Squad-Head-SHA: 24dec51

@jpapiez

jpapiez commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator Author

Squad-Reviewer: vasquez
Squad-Verdict: APPROVE
Squad-Head-SHA: 24dec51

Copilot AI added 2 commits August 26, 2026 18:38
…rm-infrastructure-tests

# Conflicts:
#	docs/CI.md
#	scripts/ci/select-dotnet-tests.sh
…les.Inventory

The merge with origin/development (Phase 16, Farm.Modules.Inventory, PR #2081)
moved FilamentCoverageControllerTests.cs out of Farm.Web.Api.Tests, whose
GlobalUsings.cs global-using's Farm.Testing.Shared for the whole project. The
new Farm.Modules.Inventory.Tests project lacks that global using, so the
already-correctly-merged AppDbTestHelpers call site (renamed from
TestInfrastructure.TestHelpers by this PR's own cohort-C move, commit
0a62464) failed to resolve. Add the explicit using directive to this one
file; no other file references AppDbTestHelpers from Inventory.Tests.
@jpapiez

jpapiez commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator Author

Squad-Reviewer: hicks
Squad-Verdict: APPROVE
Squad-Head-SHA: 3acd07d

Round-three resolution review: APPROVE.

  • docs/CI.md accurately mirrors the final selector for api, infra, inventory, tests_api, and tests_inventory; no affected row is omitted or contradictory.
  • The deliberate has_api / has_infra split is fully preserved. The infra leg does not re-add Farm.Web.Api.Tests or Farm.Web.IntegrationTests; its comment matches its array, and the eight reverse-dependent module suites are correctly counted (SmartPlug, PrintQueue, Maintenance, Calibration, Gcode, Identity, Inventory, Administration).
  • Inventory classification, build triggering, test selection, and reason reporting are wired consistently, including both has_tests_infra and has_tests_inventory in the build condition.
  • No conflict markers remain in the reviewed files.
  • using Farm.Testing.Shared; is the minimal correct fix: AppDbTestHelpers is defined uniquely in that namespace, this is the only Inventory test file using it, and no competing imported AppDbTestHelpers type was found.

Per review instructions, I performed read-only diff inspection only and did not build or run tests.

@jpapiez

jpapiez commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator Author

Squad-Reviewer: vasquez
Squad-Verdict: APPROVE
Squad-Head-SHA: 3acd07d

The merge conflict resolution and the subsequent test fix are correct and complete.

  • docs/CI.md correctly unions the changes from both branches, capturing all test permutations for api, infra, and tests_api.
  • scripts/ci/select-dotnet-tests.sh accurately preserves this branch's deliberate split of has_api and has_infra while properly inserting the inventory logic from development into both. The test count comment was updated correctly to "eight".
  • The addition of using Farm.Testing.Shared; in FilamentCoverageControllerTests.cs is the precise, minimal fix required for the CS0103 error caused by the loss of the global using during the file move.

@jpapiez

jpapiez commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator Author

Squad-Reviewer: bishop
Squad-Verdict: APPROVE
Squad-Head-SHA: 3acd07d

Bishop — round 3 merge-resolution review (read-only, no build/test run)

Scope: git diff 24dec5139 3acd07df9 over docs/CI.md, scripts/ci/select-dotnet-tests.sh, src/tests/Farm.Modules.Inventory.Tests/Controllers/FilamentCoverageControllerTests.cs. I did not build or run any suite, per dispatch.

1. scripts/ci/select-dotnet-tests.sh — split preserved, union correct

  • The deliberate split is genuinely intact. if (( has_api )) (L917) and if (( has_infra )) (L932) are separate blocks; the only surviving has_api || has_infra is the want_dotnet_build guard (L905), which is correct and always was.
  • Cross-checked git diff aaa651e9c 3acd07df9 -- scripts/ci/select-dotnet-tests.sh: the branch-side delta vs development''s tip is exactly the Phase 5 intent (infra leg rewrite, tests_infra bucket + classifier arm + has_tests_infra var/reason, Farm.Infrastructure.Tests added to backend_core/backend_plugin/slicer/migrations_app/migrations_slcr). Nothing from Phase 16: Extract Farm.Modules.Inventory from Farm.Web.Api #2081 was dropped: every inventory/tests_inventory line development added (header docs, classify_path arms, locals, dispatch arms, has_inventory block, has_tests_inventory block, both reason lines) is present.
  • Comments match arrays. has_api array = 6 names, comment names Identity + Inventory + Administration reverse-deps — matches. has_infra array = 11 names; the "all eight of their test projects" count refers to the modules enumerated after "also reference Farm.Infrastructure directly" (SmartPlug, PrintQueue, Maintenance, Calibration, Gcode, Identity, Inventory, Administration = 8) — correct; the remaining 3 entries (Infrastructure, Slicer.Module, OrcaSlicer.Worker) are called out separately in the same comment. tests_api array = 4, comment updated to say "both" for Identity+Inventory — matches.
  • want_dotnet_build union is right: has_tests_infra (branch) and has_tests_inventory (dev) both present, each exactly once, no dropped disjunct.
  • No conflict-marker artifacts in any of the three files.

2. docs/CI.md — accurate against the final script

Row-by-row against the resolved test_names arrays: api (6), infra (11, same order), tests_api (4), plus new inventory and tests_inventory rows — all exact, no omission, no stale row. The infra row correctly still omits Farm.Web.Api.Tests/Farm.Web.IntegrationTests, consistent with the #2033 prose at L139–148 and with case_infra_change / case_infra_change_does_not_build_web_api_test_leg in the selector test suite (assert_not_contains "Farm.Web.Api.Tests" — and Farm.Modules.Inventory.Tests is not a substring of it, so no false pass). The new inventory prose paragraph correctly explains why api/infra/tests_api all carry Farm.Modules.Inventory.Tests.

3. using Farm.Testing.Shared; — right, minimal, complete, no collision risk

  • Root cause is correctly diagnosed: global using Farm.Testing.Shared; lives in Farm.Web.Api.Tests/GlobalUsings.cs and global usings are per-compilation, not transitive over ProjectReference. Farm.Modules.Inventory.Tests has no GlobalUsings.cs of its own. Not a workaround.
  • Complete: grepped all 11 .cs files under Farm.Modules.Inventory.Tests for AppDbTestHelpers|TestHelpers|TestAuthHandler|TestPaths|TestSqlitePragmaEnforcer|BlockOutboundHttpFilter|TestTiming — the only hits are the new using (L12) and the single AppDbTestHelpers.GetUnknownCatalogIdsAsync(ctx) call site (L301) in this file. No other file in the project depends on the missing global using.
  • No ambiguity risk: Farm.Testing.Shared''s public surface (AppDbTestHelpers, BlockingOutboundHandler, BlockOutboundHttpFilter, HostFixture<T>, ProviderDatabaseTestCollection, TestAuthHandler, TestPaths, TestSqlitePragmaEnforcer, TestTimingAttribute, TimingReportingTestFramework) shares no simple name with anything in Farm.Web.Api.Tests.TestInfrastructure or the Farm.Infrastructure.* namespaces this file imports, and none of those names is otherwise used here. CS0104 is not reachable.
  • The using is inserted in correct alphabetical position within the existing block.

Non-blocking observations (no action required in this PR)

  1. The infra leg still transitively builds Farm.Web.Api via Farm.Modules.Inventory.TestsFarm.Web.Api.TestsFarm.Web.Api. That is not a regression from this resolution — Farm.Modules.Identity.Tests and Farm.Modules.Administration.Tests already had that shape in the approved base at 24dec5139, and the Phase 5: Create Farm.Infrastructure.Tests #2033 acceptance criterion is scoped to the Farm.Infrastructure.Tests leg itself, which the docs and test comments state explicitly. Adding Inventory follows the same precedent.
  2. Farm.Modules.Inventory.Tests having no GlobalUsings.cs means any future file moved out of Farm.Web.Api.Tests into it will hit the same trap. A one-line GlobalUsings.cs mirroring the API test project would pre-empt that, but for a single call site the explicit using is the smaller, more surgical change and is the correct call here.
  3. The moved file retains namespace Farm.Web.Api.Tests.Controllers while living in Farm.Modules.Inventory.Tests — inherited verbatim from Phase 16: Extract Farm.Modules.Inventory from Farm.Web.Api #2081, out of scope for this resolution.

Evidence I could not verify by reading

Compilation success, Farm.Infrastructure.Tests 1933/1939, Farm.Web.Api.Tests 3681/3681, and test-select-dotnet-tests.sh 122/122 are author-reported; I was instructed not to build or run tests, so I take those as reported rather than independently confirmed. Nothing I read contradicts them, and the selector-suite expectations I read (case_infra_change, case_infra_change_does_not_build_web_api_test_leg, case_test_only_api, case_inventory_change, case_test_only_inventory) are all satisfied by the resolved script as written.

Verdict: APPROVE. The union is correct and complete, the deliberate has_api/has_infra split survived the merge intact, the docs mirror the script exactly, and the build fix is the right minimal one.

…rm-infrastructure-tests

# Conflicts:
#	scripts/ci/dotnet-test-manifest.json
#	scripts/ci/select-dotnet-tests.sh
@jpapiez

jpapiez commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator Author

Squad-Reviewer: hicks
Squad-Verdict: REQUEST_CHANGES
Squad-Head-SHA: 21cdfa1

Blocking: docs/CI.md is not the union described for the resolved selector. The resolved script has narrow devices and tests_devices buckets and adds Farm.Modules.Devices.Tests to the has_infra reverse-dependency list, but the authoritative bucket table has no devices row, no tests_devices row, and its infra row omits Farm.Modules.Devices.Tests. Update those three table entries so the documentation matches select-dotnet-tests.sh.

The remaining resolution is coherent on inspection: has_api/has_infra remain separate; infra excludes the web-host test legs while retaining Infrastructure/Slicer/Orca plus all nine module test projects; every requested build flag appears once. Dropping Filters from the API shard is correct, and Discovery/Logging/Migrations/Network/IAppSettingTests were moved into Farm.Infrastructure.Tests and are covered by its shard filters, so they should not remain in the API shard. No builds or tests were run per review instructions.

@jpapiez

jpapiez commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator Author

Squad-Reviewer: bishop
Squad-Verdict: APPROVE
Squad-Head-SHA: 21cdfa1

Round 4 merge-conflict resolution review (scope: git diff fe0b848cf 21cdfa12b -- docs/CI.md scripts/ci/dotnet-test-manifest.json scripts/ci/select-dotnet-tests.sh). Read-only; no builds or test runs performed.

dotnet-test-manifest.json — correct

Verified the resolved Farm.Web.Api.Tests shard prefixes against the actual project tree on disk. The 21 remaining directories are Backends, Calibration, Capabilities, Controllers, Data, DataManagement, Dispatch (core), Health, Hubs, Infrastructure, Integration, Locations, Middleware, Monitoring (infra), Security, Services, Slicing, Startup, TestHelpers, TestInfrastructure, Util + root files (services). That is an exact, exhaustive, mutually-exclusive partition — no orphan, no double-count.

  • Dropping "Filters" is correct: no Filters directory exists anywhere under src/tests/Farm.Web.Api.Tests (moved to Farm.Modules.Devices.Tests by Extract Farm.Modules.Devices from Farm.Web.Api (Phase 15) #2083).
  • Dropping Discovery/Logging/Migrations/Network (and Builders/Domain/Dtos/Repositories from core/services) is correct: those directories now live under Farm.Infrastructure.Tests, and each is claimed by exactly one of its six shards.
  • Dropping the FullyQualifiedName~Farm.Infrastructure.Settings.Tests.IAppSettingTests. clause is correct: that file moved to src/tests/Farm.Infrastructure.Tests/Infrastructure/IAppSettingTests.cs and its namespace changed to Farm.Infrastructure.Tests.Infrastructure, so it is now matched by the slicing shard's Farm.Infrastructure.Tests.Infrastructure. prefix. No test is silently orphaned.
  • Farm.Infrastructure.Tests shard coverage re-verified: all 14 top-level dirs and all 13 Services/* subdirs claimed exactly once; root holds only GlobalUsings.cs, so no (root) shard is needed. Trailing-dot prefixes prevent the Data./DataManagement. and Infrastructure.Repositories/Repositories collisions.
  • JSON parses; 17 entries, no duplicate name or leg.

select-dotnet-tests.sh — correct

  • want_dotnet_build condition (L925-932): 32 flags, each appearing exactly once. Union is development's set plus has_tests_infra — nothing dropped, nothing duplicated. has_tests_modules/has_tests_shared/has_tests_other are legitimately absent because they short-circuit to emit_full_safe earlier (L894/L900/L908).
  • has_infra block (L953-975), read from the file on disk rather than the hunk: Farm.Infrastructure.Tests, Farm.Slicer.Module.Tests, Farm.OrcaSlicer.Worker.Tests are all present, plus the nine module test projects (SmartPlug, PrintQueue, Maintenance, Calibration, Devices, Gcode, Identity, Inventory, Administration). The "all nine" comment is arithmetically correct, and Farm.Modules.Devices.Tests was correctly absorbed from development.
  • Structural invariant intact: if (( has_api )) (L938) and if (( has_infra )) (L953) remain two separate blocks — not recombined — and the has_infra block still omits Farm.Web.Api.Tests/Farm.Web.IntegrationTests. The acceptance criterion for Phase 5: Create Farm.Infrastructure.Tests #2033 is preserved, and case_infra_change_does_not_build_web_api_test_leg still asserts it.

docs/CI.md — clean auto-merge

Row-set comparison against fe0b848cf: head = development's 53 rows + tests_infra, nothing lost, no genuine duplicate (the apparent frontend repeat is a second, unrelated table and pre-exists on development).

Non-blocking observations (not introduced by this resolution)

  1. docs/CI.md never mentions Farm.Modules.Devices.Tests — there is no devices bucket row and the infra row omits it — even though select-dotnet-tests.sh selects it. Confirmed identical on fe0b848cf, so this is upstream drift from Extract Farm.Modules.Devices from Farm.Web.Api (Phase 15) #2083, not this branch. Worth a follow-up issue against the Devices phase.
  2. The services shard filter still carries FullyQualifiedName~Farm.Web.Api.Tests.PasswordPolicyControllerTests. but no root-level file of that name exists. Harmless (matches nothing; cannot double-count a Controllers-namespaced class) and unchanged by this diff.
  3. case_infra_bucket asserts eight module legs but not Farm.Modules.Devices.Tests. assert_contains-only, so it still passes, and development's test file has the same gap — no coverage regression from this branch.

No blocking issues. The resolution is correct and complete.

@jpapiez

jpapiez commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator Author

Squad-Reviewer: hicks
Squad-Verdict: APPROVE
Squad-Head-SHA: 21cdfa1

The prior REQUEST_CHANGES is withdrawn. Git history establishes that the missing Devices rows in docs/CI.md predate this merge resolution and were never introduced by PR #2083, so they are outside this round's scoped review. The actual conflict resolutions are correct: API shard ownership follows the relocated tests, build flags are complete, and the separate has_api/has_infra structure preserves the intended infra-only selection behavior. No builds or tests were run.

@jpapiez

jpapiez commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator Author

Squad-Reviewer: vasquez
Squad-Verdict: REQUEST_CHANGES
Squad-Head-SHA: 21cdfa1

I have reviewed the fourth-round merge conflict resolution at \21cdfa12b.

There is a blocking issue in \docs/CI.md.
Although the auto-merge was reported as clean, \docs/CI.md\ is missing the \devices\ and \ ests_devices\ rows entirely. The incoming additions from PR #2083 were silently dropped or overwritten by Git during the merge. You need to manually restore the \devices\ and \ ests_devices\ rows to the bucket table so it matches the buckets defined in \select-dotnet-tests.sh.

The manual conflict resolutions are correct:

  1. **\scripts/ci/dotnet-test-manifest.json**: Dropping "Filters"\ from \Farm.Web.Api.Tests's \infra\ shard is correct since the folder is gone. Furthermore, you correctly dropped (rather than kept) \Discovery, \Logging, \Migrations, \Network, and \IAppSettingTests\ from the \Farm.Web.Api.Tests\ shard. This is the exact right resolution because this branch moved those namespaces into \Farm.Infrastructure.Tests, where they are now safely assigned to its own \printers\ and \�dmin\ shards without being orphaned.
  2. **\scripts/ci/select-dotnet-tests.sh**: The \want_dotnet_build\ flags are correctly unioned with no duplicates. The \has_infra\ block correctly counts 9 module test projects (successfully adding \Farm.Modules.Devices.Tests), maintains the \has_api\ split, and excludes \Farm.Web.Api.Tests\ as required by the acceptance criteria.

Please fix \docs/CI.md\ and re-request review.

@jpapiez

jpapiez commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator Author

Squad-Reviewer: vasquez
Squad-Verdict: APPROVE
Squad-Head-SHA: 21cdfa1

I have reviewed the git history evidence regarding docs/CI.md. Since the devices and ests_devices rows were never added by the upstream PR #2083 (the Devices extraction) on development, they were not dropped by this merge conflict resolution. I agree this is a pre-existing omission on the base branch and therefore out of scope for this PR's merge review.

The manual conflict resolutions in scripts/ci/dotnet-test-manifest.json and scripts/ci/select-dotnet-tests.sh remain perfectly correct. I am superseding my previous verdict to APPROVE.

(Note: We should file a quick follow-up PR against development to add the missing docs rows for Devices.)

@jpapiez
jpapiez merged commit 74ca273 into development Aug 27, 2026
57 checks passed
@jpapiez
jpapiez deleted the dev/jpapiez/farm-infrastructure-tests branch August 27, 2026 03:29
jpapiez pushed a commit that referenced this pull request Aug 27, 2026
Merges #2089 (alias normalization + hash-conflict handling) and #2084
(Farm.Infrastructure.Tests). Resolved the sole conflict in
ProfileFamilyServiceTests.cs (using-directive union; both sides' tests
kept). Verified RemoveModelAliasAsync, EnsureNoLastCoverageLossAsync,
and the rename flow remain consistent with #2089's normalized matching
and constraint handling.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
jpapiez pushed a commit that referenced this pull request Aug 27, 2026
…sts (#2034)

Bishop (blocking):
- Add missing 'Upload Farm.Backend.Plugins.Tests build' step to
  .github/workflows/ci.yml, following the exact pattern PR #2084 added for
  Farm.Infrastructure.Tests. Without this, any CI run whose selector emits
  Farm.Backend.Plugins.Tests would fail at 'Download test build'.
- docs/CI.md 'tests_infra' row incorrectly claimed selecting
  Farm.Backend.Plugins.Tests too; select-dotnet-tests.sh's has_tests_infra
  block only ever appends Farm.Infrastructure.Tests. Reverted (same finding
  independently raised by Hicks).
- Move an orphaned Sdcp cohort test file
  (src/tests/Farm.Web.Api.Tests/SdcpClientBusyTests.cs) that the path-based
  scope (Backends/**, Services/{...}/**) missed because it sat at the
  project root. Renamed to SdcpClientIsPrintingStatusTests.cs / class
  SdcpClientIsPrintingStatusTests to avoid colliding with the unrelated,
  already-moved Backends/SdcpClientBusyTests.cs (different namespace-scoped
  class, same simple name).

Hicks (blocking):
- src/farm-web.sln had a spurious CRLF inserted after the UTF-8 BOM by the
  earlier 'dotnet sln add' invocation, pushing the solution header to line
  2. Removed the stray bytes so the header matches upstream exactly.
- docs/CI.md 'infra' row was missing Farm.Modules.Devices.Tests, which
  select-dotnet-tests.sh's has_infra block does select (pre-existing gap,
  fixed opportunistically since this PR already edits that row).

Verified after these fixes:
- dotnet build ./farm-web.sln -c Debug: 0 errors
- Farm.Backend.Plugins.Tests: 279 passed (271 + 8 from the newly moved file)
- Farm.Web.Api.Tests: 3396 passed (3404 - 8)
- Total 3675, matching the pre-move baseline exactly
- scripts/ci/tests/test-select-dotnet-tests.sh: 130/130 passed
- dotnet format --verify-no-changes on touched files: clean

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
jpapiez added a commit that referenced this pull request Aug 27, 2026
## Summary

Splits the backend-plugin test cohort (Backends/** +
Services/{PrusaLink,Sdcp,FlashForge,TestEmulator}/**) out of
`Farm.Web.Api.Tests` into a new `Farm.Backend.Plugins.Tests` project,
per Phase 6 of epic #2019 (decomposing `Farm.Web.Api` into
vertical-slice module assemblies with a matching test-project split).
Follows the pattern established by the direct predecessor, Phase 5
(#2084, `Farm.Infrastructure.Tests`).

Closes #2034

## What changed

- New
`src/tests/Farm.Backend.Plugins.Tests/Farm.Backend.Plugins.Tests.csproj`,
referencing `Farm.Infrastructure` + the backend plugin assemblies
(`Farm.Backend.Plugin.{Core,Moonraker,PrusaLink,Sdcp,FlashForge,TestEmulator}`)
— **not** `Farm.Web.Api`.
- 20 test files moved (19 from the declared `Backends/**` /
`Services/{PrusaLink,Sdcp,FlashForge,TestEmulator}/**` scope, plus one
orphaned root-level file, `SdcpClientBusyTests.cs`, that tested
`SdcpClient.IsPrintingStatus` but sat outside the path-based scope —
renamed to `SdcpClientIsPrintingStatusTests.cs` to avoid a class-name
collision with the unrelated, already-moved
`Backends/SdcpClientBusyTests.cs`). All moves are pure
namespace/class-name relocations — zero logic changes.
- `InternalsVisibleTo("Farm.Backend.Plugins.Tests")` added to
`Farm.Backend.Plugin.{FlashForge,Moonraker,Sdcp,TestEmulator}` (Core and
PrusaLink need no grant — their only internals are ctor overloads
already shadowed by public ones).
- Added to `src/farm-web.sln`.
- Registered in the CI test manifest
(`scripts/ci/dotnet-test-manifest.json`): `pathPrefixes`,
`dependsOnProjects`, `defaultFilter`, `leg`. While fixing a related
manifest nit, also removed two now-stale fragments from
`Farm.Web.Api.Tests`'s own shard definitions (a dead `Backends`
namespace prefix/filter left over from this same relocation, and a dead
`SdcpClientBusyTests` filter fragment) —
`scripts/ci/tests/test-dotnet-test-manifest.sh` was failing on the stale
`Backends` entry before this fix.
- `scripts/ci/select-dotnet-tests.sh` /
`scripts/ci/tests/test-select-dotnet-tests.sh` extended with 5 new
positive/negative/full-safe cases for the new project, following the
exact `assert_contains`/`assert_not_contains` convention used by the
Phase 5 sibling cases.
- `.github/workflows/ci.yml`: added the `Upload
Farm.Backend.Plugins.Tests build` artifact step (same shape as the Phase
5 `Farm.Infrastructure.Tests` step) — without it, any CI run selecting
this project would fail at "Download test build".
- `docs/CI.md` bucket table updated: `infra`, `backend_core`,
`backend_plugin` rows now list `Farm.Backend.Plugins.Tests`; new
`tests_backend_plugins` row added. Also opportunistically fixed a
pre-existing gap in the `infra` row (`Farm.Modules.Devices.Tests` was
missing, despite being selected by the script) since the row was already
being edited.
- Zero product-code diff: only test-project files, `.sln`, CI
scripts/config, and docs changed.
- No EF Core migrations — no entity types moved.
- Total test count across all assemblies unchanged (files moved, not
duplicated or dropped): `Farm.Backend.Plugins.Tests` 279 passed,
`Farm.Web.Api.Tests` 3396 passed, total 3675 — matches the pre-move
baseline exactly.

## Validation

- `dotnet build ./farm-web.sln -c Debug` — 0 errors (no new warnings).
- `dotnet test
./tests/Farm.Backend.Plugins.Tests/Farm.Backend.Plugins.Tests.csproj` —
279 passed, 0 failed.
- `dotnet test ./tests/Farm.Web.Api.Tests/Farm.Web.Api.Tests.csproj`
(full regression) — 3396 passed, 0 failed.
- `dotnet format ./farm-web.sln --verify-no-changes` (scoped to touched
projects) — clean.
- `bash scripts/ci/tests/test-select-dotnet-tests.sh` — 130/130 passed.
- `bash scripts/ci/tests/test-dotnet-test-manifest.sh` — PASS.

## Review

Reviewed by Bishop (claude-opus-5), Hicks (gpt-5.6-sol), and Vasquez
(gemini-3.1-pro-preview) — three rounds of adversarial review across
three different model vendors, converging on unanimous APPROVE at the
current head SHA. Verdicts posted below.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

squad Squad triage inbox — Lead will assign to a member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Phase 5: Create Farm.Infrastructure.Tests

2 participants