Skip to content

Run the test suite on CI, and give it something to run - #15

Merged
wasabii merged 2 commits into
mainfrom
claude/auth-zero-seek-operator-sync-6y67xf
Sep 6, 2026
Merged

wasabii merged 2 commits into
mainfrom
claude/auth-zero-seek-operator-sync-6y67xf

Conversation

@wasabii

@wasabii wasabii commented Sep 6, 2026 •

Copy link
Copy Markdown
Contributor

Follow-up to #14, from the same auth0-operator sync review. That PR left the CI test job out; this adds it, fixes the reason the tests artifact was empty, and gives the suite its first tests.

The tests artifact was never real

src/dist-tests/dist-tests.msbuildproj still listed IKVM.ByteCode.Tests targets for net472/net6.0/net7.0/net8.0 — projects that do not exist in this repository — left over from the template it was copied from. It resolved no project file, published nothing, and CI happily tarred and uploaded an empty directory. #14's runs show the artifact at 247 bytes, with the tar listing just tests/. It now points at Alethic.Seq.Operator.Tests on net10.0.

A test job

Matrixed over win-x64, linux-x64 and linux-arm64. The tests use Microsoft.Testing.Platform, so the published assembly is executed directly with dotnet exec rather than through dotnet test, which fails on .NET 10. Results are uploaded as TRX per matrix leg.

release now needs test instead of build, so a red suite blocks publishing to ghcr.

First tests: the finalizer identifiers

#14 moved the finalizers to version-free names with legacy shims for the retired identifiers. That mechanism is worth pinning, because getting it wrong is silent and expensive: KubeOps derives a finalizer's identifier from its class name, and the reconciler inspects Finalizers()[0] only, returning without removing it when nothing is registered under that identifier. A rename, or a deleted shim, wedges every live entity carrying the old identifier in Terminating forever. Nothing about that fails at compile time.

Six tests:

test catches
CurrentIdentifiersAreExactlyAsReleased renaming a current finalizer class
RetiredIdentifiersAreStillRegistered deleting a legacy shim
CurrentIdentifierIsNeverAlsoRetired an entry that is attached and stripped on the same pass, so reconciliation never settles
IdentifiersFitTheKubernetesLimit an identifier past the 63 characters KubeOps truncates at
EveryFinalizerClassIsAccountedForInTheTable a finalizer class the table does not list
EveryTableEntryHasAFinalizerClass a table entry no class produces, which KubeOps could never resolve

The last two check the table and the classes agree in both directions, by reflecting over every IEntityFinalizer<> implementation in the operator assembly and reproducing KubeOps' identifier derivation. EntityFinalizers derives its strings from the finalizer types, so it cannot drift from what KubeOps registers — these assert it cannot drift from what was actually released either.

All six pass on all three platforms, so #14's finalizer table is now verified against the shipped classes rather than assumed correct.

A packaging defect the tests exposed

Referencing the operator from a test project turned up a real problem that predates this PR. SelfContained was set project-wide on the operator, so even a plain, RID-less build produced RID-specific output, and the operator's assembly reached a referencing project only as a RID-scoped asset — resolvable under the two declared RuntimeIdentifiers (win-x64, linux-x64) and absent everywhere else.

The symptom was precise: one tests artifact, built once and shared by every matrix leg, in which all six tests passed on win-x64 and linux-x64 and all six failed on linux-arm64 with FileNotFoundException: Could not load file or assembly 'Alethic.Seq.Operator'. Nothing else in the repository referenced the operator, so this had never surfaced.

SelfContained is now conditioned on a RuntimeIdentifier actually being set. The container publish always sets one (dist-containers passes RuntimeIdentifier=linux-x64), so the image is unchanged; plain builds and the test reference are framework-dependent and architecture-neutral.

The image was verified unchanged rather than assumed: the containers artifact is 99,205,905 bytes after the change against 99,205,503 before — a 402-byte delta on 99 MB, attributable to the version string. A framework-dependent publish would have collapsed it by roughly an order of magnitude.

The test project also sets ValidateExecutableReferencesMatchSelfContained=false, since a non self-contained executable cannot reference a self-contained one (NETSDK1150) and the container publish still produces one.

Testing

Same caveat as #14: no .NET SDK is available in the authoring environment and egress policy blocks every SDK download host, so CI is the compiler. Project and workflow files are parsed locally with real XML and YAML parsers before pushing, and the workflow's job graph was checked (build → test → release).

🤖 Generated with Claude Code

https://claude.ai/code/session_01M3mJmvzVTi5AtFhQComUcs

wasabii and others added 2 commits September 6, 2026 20:10
The tests artifact has always been empty: dist-tests still listed the
IKVM.ByteCode.Tests targets from the template it was copied from, so it
published nothing, and no job ever executed it. Point it at
Alethic.Seq.Operator.Tests on net10.0.

Add a test job to the workflow, running the published test assembly
directly on win-x64, linux-x64 and linux-arm64. The tests use
Microsoft.Testing.Platform, so they are executed as an assembly rather
than through 'dotnet test', which fails on .NET 10. Release now waits on
test rather than build, so a red suite blocks publishing.

Give the suite its first tests, covering the finalizer identifiers. These
are the strings written into metadata.finalizers, and KubeOps derives them
from the finalizer class name; the reconciler inspects Finalizers()[0]
only and returns without removing an identifier nothing is registered for.
Renaming a finalizer or deleting a legacy shim therefore wedges every live
entity carrying the old identifier in Terminating. The tests pin the exact
wire strings, assert current and retired never overlap, hold both within
the 63 character limit, and check the table and the finalizer classes
agree in both directions, so neither a rename nor a deleted shim can pass
silently.

The test project references the operator, which sets SelfContained;
ValidateExecutableReferencesMatchSelfContained is disabled there so the
reference is allowed without changing how the operator is published.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M3mJmvzVTi5AtFhQComUcs
All six tests failed on linux-arm64 while passing on win-x64 and
linux-x64, from the same artifact, with FileNotFoundException loading the
operator assembly. The tests artifact is built once and shared by every
leg, so an identical file set resolving on two architectures and not the
third is RID-scoped asset resolution: SelfContained was set project-wide,
which makes even a plain build produce RID-specific output, so the
assembly reaches a referencing project only under the RIDs listed in
RuntimeIdentifiers, win-x64 and linux-x64. linux-arm64 had nothing to
load.

Condition SelfContained on a RuntimeIdentifier being set. The container
publish always sets one, so the published image is unchanged; a plain
build and the test project's reference are now framework-dependent and
architecture-neutral.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M3mJmvzVTi5AtFhQComUcs
@wasabii
wasabii merged commit 27cf675 into main Sep 6, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant