From f9b0d711c1b07d0d2e1921f6e1c9df231727de38 Mon Sep 17 00:00:00 2001 From: baku-ccron Date: Fri, 21 Aug 2026 18:16:07 +0000 Subject: [PATCH 1/2] fix(test): drop vm.skip from the tagged deploy constants check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The org-wide `no-ignored-tests` gate bans `vm.skip` outright, "conditional or otherwise", and main has exactly one: ./test/src/lib/deploy/LibDecimalFloatDeployTaggedConstants.t.sol:24 It fails both `rainix-rs-static` and the `static` job of `rainix-sol`, and it also violates this repo's own CLAUDE.md ("No skipped tests"). The skip was not parking a failing test: it fired only when the FFI query to api.soldeer.xyz failed, so it was network tolerance. Deleting it outright would trade a banned construct for a flaky test; deleting the test would drop the check that a published tag carries its deploy constants. So the check is split by what it depends on instead: structural every version suffix carrying any pinned constant carries all four. Pure file inspection, so it always runs. registry every version published to soldeer is pinned. Needs api.soldeer.xyz. `script/check-published-deploy-constants.sh` grows `--offline` and `--lib` so the structural half can be asserted deterministically, and the test file gains two tests that do exactly that. The registry test keeps its assertion; when the registry is unreachable the script emits SKIP only after the structural half has passed, and the test logs that reason and returns — a pass on what was actually checked, not a renamed skip. Ports the approach from rainlanguage/rain.math.float#265, which fixes the identical file in the library half of the split. --- script/check-published-deploy-constants.sh | 113 ++++++++++++++---- .../fixtures/half-pinned-deploy-constants.txt | 16 +++ ...LibDecimalFloatDeployTaggedConstants.t.sol | 64 ++++++++-- 3 files changed, 161 insertions(+), 32 deletions(-) create mode 100644 test/fixtures/half-pinned-deploy-constants.txt diff --git a/script/check-published-deploy-constants.sh b/script/check-published-deploy-constants.sh index 2faba9b..1fd18d1 100644 --- a/script/check-published-deploy-constants.sh +++ b/script/check-published-deploy-constants.sh @@ -2,50 +2,119 @@ # SPDX-License-Identifier: LicenseRef-DCL-1.0 # SPDX-FileCopyrightText: Copyright (c) 2020 Rain Open Source Software Ltd # -# Prints "OK" iff every version published to the soldeer registry for -# `rain-math-float-deploy` has a full suite of pinned deploy constants in -# LibDecimalFloatDeploy.sol: a log-tables address + codehash and a DecimalFloat -# address + codehash, each suffixed with the version. +# Checks that the per-version deploy constants pinned in LibDecimalFloatDeploy.sol +# are complete. Each published version needs a full suite: a log-tables address + +# codehash and a DecimalFloat address + codehash, each suffixed with the version +# (dots replaced by underscores). +# +# Two halves, split by what they depend on: +# +# 1. STRUCTURAL (offline). Every version suffix that carries any pinned +# constant must carry all four. Catches a half-written release snapshot. +# Pure file inspection, so it is deterministic and always runs. +# 2. REGISTRY (online). Every version published to the soldeer registry must +# carry a pinned suite, so publishing a tag without pinning its constants +# is caught. Needs api.soldeer.xyz. +# +# Usage: +# check-published-deploy-constants.sh [--lib ] [--offline] +# +# --lib file to inspect (default src/lib/deploy/LibDecimalFloatDeploy.sol) +# --offline run the structural half only, and never touch the network. +# Lets a test assert the structural half deterministically +# instead of depending on whether the registry answered. # # Consumed by LibDecimalFloatDeployTaggedConstants.t.sol via FFI. Output is one # of: -# OK - every published version has its full constant suite +# OK - every half that ran, passed # MISSING: - one or more expected constants are absent -# SKIP: - the registry could not be reached (nothing verified) +# SKIP: - default mode only: the structural half passed but the +# registry was unreachable, so that half did not run # # Always exits 0 so the test sees the message rather than an ffi failure. +set -uo pipefail + lib="src/lib/deploy/LibDecimalFloatDeploy.sol" +offline=0 -versions=$( - curl -fsS --connect-timeout 5 --max-time 20 --retry 2 --retry-delay 1 \ - "https://api.soldeer.xyz/api/v1/revision?project_name=rain-math-float-deploy" 2>/dev/null \ - | grep -oE '"version":"[0-9][0-9.]*"' | cut -d'"' -f4 | sort -u -) +while [ "$#" -gt 0 ]; do + case "$1" in + --lib) + lib="${2:-}" + if [ -z "$lib" ]; then + printf 'MISSING: --lib requires a path' + exit 0 + fi + shift 2 + ;; + --offline) + offline=1 + shift + ;; + *) + printf 'MISSING: unknown argument %s' "$1" + exit 0 + ;; + esac +done -if [ -z "$versions" ]; then - printf 'SKIP: could not fetch published soldeer versions' +if [ ! -f "$lib" ]; then + printf 'MISSING: no such file %s' "$lib" exit 0 fi -# The deploy constants that must be pinned for every published version, suffixed -# with the version (dots replaced by underscores). +# The deploy constants that must be pinned for every version. bases="ZOLTU_DEPLOYED_LOG_TABLES_ADDRESS \ LOG_TABLES_DATA_CONTRACT_HASH \ ZOLTU_DEPLOYED_DECIMAL_FLOAT_ADDRESS \ DECIMAL_FLOAT_CONTRACT_HASH" missing="" -for v in $versions; do - suffix=$(printf '%s' "$v" | tr . _) - for b in $bases; do - name="${b}_${suffix}" - grep -qE "constant ${name} =" "$lib" || missing="${missing} ${name}" + +# Append every `_` absent from the lib to $missing. +check_suffixes() { + for suffix in $1; do + for b in $bases; do + name="${b}_${suffix}" + grep -qE "constant ${name} =" "$lib" || missing="${missing} ${name}" + done done -done +} + +# 1. Structural half. Collect every version suffix carrying at least one pinned +# constant, then demand the whole suite for each. Requiring `_[0-9]` after the +# base keeps the un-suffixed "current" constants out of the suffix set. +pinned_suffixes=$( + for b in $bases; do + grep -oE "constant ${b}_[0-9][0-9_]* =" "$lib" \ + | sed -E "s/^constant ${b}_//; s/ =\$//" + done | sort -u +) +check_suffixes "$pinned_suffixes" + +# 2. Registry half. +versions="" +if [ "$offline" -eq 0 ]; then + versions=$( + curl -fsS --connect-timeout 5 --max-time 20 --retry 2 --retry-delay 1 \ + "https://api.soldeer.xyz/api/v1/revision?project_name=rain-math-float-deploy" 2>/dev/null \ + | grep -oE '"version":"[0-9][0-9.]*"' | cut -d'"' -f4 | sort -u + ) + if [ -n "$versions" ]; then + check_suffixes "$(printf '%s' "$versions" | tr . _)" + fi +fi +# An absence outranks an unreachable registry: a MISSING from the structural +# half is a real failure whether or not the registry answered. if [ -n "$missing" ]; then - printf 'MISSING:%s' "$missing" + printf 'MISSING:' + printf '%s' "$missing" | tr ' ' '\n' | grep -v '^$' | sort -u | while IFS= read -r n; do + printf ' %s' "$n" + done +elif [ "$offline" -eq 0 ] && [ -z "$versions" ]; then + printf 'SKIP: could not fetch published soldeer versions; pinned constant suites are structurally complete' else printf 'OK' fi diff --git a/test/fixtures/half-pinned-deploy-constants.txt b/test/fixtures/half-pinned-deploy-constants.txt new file mode 100644 index 0000000..c3aebff --- /dev/null +++ b/test/fixtures/half-pinned-deploy-constants.txt @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: LicenseRef-DCL-1.0 +# SPDX-FileCopyrightText: Copyright (c) 2020 Rain Open Source Software Ltd +# +# Fixture for LibDecimalFloatDeployTaggedConstantsTest. Stands in for +# LibDecimalFloatDeploy.sol and deliberately pins version 9.9.9 only HALFWAY: +# the two address constants are present, the two codehash constants are not. +# The structural half of script/check-published-deploy-constants.sh must report +# both absent codehashes for it. +# +# Deliberately NOT a .sol file: forge compiles everything under test/, and this +# is grep fodder rather than Solidity. The script only ever matches +# `constant =`, so the surrounding prose is inert. + + address constant ZOLTU_DEPLOYED_LOG_TABLES_ADDRESS_9_9_9 = address(0xc51a14251b0dcF0ae24A96b7153991378938f5F5); + + address constant ZOLTU_DEPLOYED_DECIMAL_FLOAT_ADDRESS_9_9_9 = address(0x799632d282178e770C7465cad54aDA1021A913D6); diff --git a/test/src/lib/deploy/LibDecimalFloatDeployTaggedConstants.t.sol b/test/src/lib/deploy/LibDecimalFloatDeployTaggedConstants.t.sol index ce2364c..63f98d1 100644 --- a/test/src/lib/deploy/LibDecimalFloatDeployTaggedConstants.t.sol +++ b/test/src/lib/deploy/LibDecimalFloatDeployTaggedConstants.t.sol @@ -3,25 +3,69 @@ pragma solidity =0.8.25; import {Test} from "forge-std-1.16.2/src/Test.sol"; +import {console2} from "forge-std-1.16.2/src/console2.sol"; /// @title LibDecimalFloatDeployTaggedConstantsTest -/// @notice Every version published to the soldeer registry for `rain-math-float-deploy` -/// must have a full suite of pinned deploy constants in `LibDecimalFloatDeploy`: -/// a log-tables address + codehash and a DecimalFloat address + codehash for -/// each published version. `script/check-published-deploy-constants.sh` queries -/// the live registry (via FFI) and lists any missing constants, so publishing a -/// new tag without pinning its constants fails this test. Skips if the registry -/// is unreachable rather than failing on network flakiness. +/// @notice Every version published to the soldeer registry for +/// `rain-math-float-deploy` must have a full suite of pinned deploy constants in +/// `LibDecimalFloatDeploy`: a log-tables address + codehash and a DecimalFloat +/// address + codehash for each published version. +/// +/// `script/check-published-deploy-constants.sh` splits that into a structural +/// half (every version suffix carrying any pinned constant carries all four — +/// pure file inspection) and a registry half (every published version is +/// pinned — needs api.soldeer.xyz). The structural half is asserted +/// unconditionally here, so a run that cannot reach the registry still verifies +/// something real rather than verifying nothing. contract LibDecimalFloatDeployTaggedConstantsTest is Test { + string constant SCRIPT = "script/check-published-deploy-constants.sh"; + string constant HALF_PINNED_FIXTURE = "test/fixtures/half-pinned-deploy-constants.txt"; + + /// Structural half against the committed lib. No network, so this asserts + /// on every run: a version pinned halfway fails here. + function testEveryPinnedVersionGroupIsComplete() external { + string[] memory cmd = new string[](3); + cmd[0] = "bash"; + cmd[1] = SCRIPT; + cmd[2] = "--offline"; + assertEq(string(vm.ffi(cmd)), "OK", "a pinned version is missing part of its deploy constant suite"); + } + + /// The structural half must actually detect a half-pinned version, not just + /// report OK for everything. Without this, a check that inspected nothing + /// would pass `testEveryPinnedVersionGroupIsComplete` just as happily. + function testStructuralCheckDetectsAHalfPinnedVersion() external { + string[] memory cmd = new string[](5); + cmd[0] = "bash"; + cmd[1] = SCRIPT; + cmd[2] = "--offline"; + cmd[3] = "--lib"; + cmd[4] = HALF_PINNED_FIXTURE; + assertEq( + string(vm.ffi(cmd)), + "MISSING: DECIMAL_FLOAT_CONTRACT_HASH_9_9_9 LOG_TABLES_DATA_CONTRACT_HASH_9_9_9", + "the structural check failed to report a half-pinned version" + ); + } + + /// Both halves. Publishing a soldeer tag without pinning its deploy + /// constants fails here. function testAllPublishedSoldeerTagsHaveAFullConstantSuite() external { string[] memory cmd = new string[](2); cmd[0] = "bash"; - cmd[1] = "script/check-published-deploy-constants.sh"; + cmd[1] = SCRIPT; bytes memory out = vm.ffi(cmd); - // The registry could not be reached; there is nothing to verify. + // api.soldeer.xyz was unreachable, so the registry half did not run. + // This is a pass on what was checked, NOT a skip: the structural half + // ran and passed inside the same invocation, and is asserted outright + // by testEveryPinnedVersionGroupIsComplete above. Only "is every + // PUBLISHED version pinned" is unverifiable without the network, + // because the set of published versions lives on the registry. The + // reason is logged so a green run that never reached the registry says + // so, instead of looking like a full check. if (_startsWith(out, bytes("SKIP"))) { - vm.skip(true); + console2.log(string(out)); return; } From 66dfd74fabf034d034d9729a6b464d7f9e3a5624 Mon Sep 17 00:00:00 2001 From: baku-ccron Date: Fri, 21 Aug 2026 19:19:30 +0000 Subject: [PATCH 2/2] fix(test): fail closed on a registry response that cannot be read An empty version set had two causes and one branch. `versions` was the only signal the registry half kept, so a fetch that failed and a response nobody could read were indistinguishable, and both fell through to `SKIP` - the branch the test returns early on. A change in the registry's response shape would therefore have retired the registry half permanently while every run stayed green. Fetch and parse are now separate. `registry_answered` records that a response arrived; `versions` records what could be read out of it. A fetch that failed stays `SKIP`, because the endpoint 404s for a project with no published revisions and a network that is down is not a finding. A response that arrived and yielded no version is the new `UNREADABLE`, a failure: the endpoint only answers 2xx for a project that exists, and a project exists on the registry because it has revisions, so a readable answer always names at least one version. The version scan also tolerates whitespace around the colon, so a pretty-printed response reads as the response it is rather than as an unreadable one. None of that was assertable without a network, so the script grows `--registry-response `, which feeds it a file in place of a fetch and is mutually exclusive with `--offline`. Three fixtures and three tests drive the registry half offline: a published version with no pinned suite is reported by name, the same response pretty-printed reads identically, and a response whose revisions name no version fails. Co-Authored-By: Claude Opus 5 (1M context) --- script/check-published-deploy-constants.sh | 81 ++++++++++++++++--- .../registry-response-pretty-printed.txt | 35 ++++++++ .../registry-response-unpinned-version.txt | 20 +++++ .../fixtures/registry-response-unreadable.txt | 22 +++++ ...LibDecimalFloatDeployTaggedConstants.t.sol | 63 +++++++++++++++ 5 files changed, 212 insertions(+), 9 deletions(-) create mode 100644 test/fixtures/registry-response-pretty-printed.txt create mode 100644 test/fixtures/registry-response-unpinned-version.txt create mode 100644 test/fixtures/registry-response-unreadable.txt diff --git a/script/check-published-deploy-constants.sh b/script/check-published-deploy-constants.sh index 1fd18d1..b81b1b4 100644 --- a/script/check-published-deploy-constants.sh +++ b/script/check-published-deploy-constants.sh @@ -18,18 +18,29 @@ # # Usage: # check-published-deploy-constants.sh [--lib ] [--offline] +# [--registry-response ] # # --lib file to inspect (default src/lib/deploy/LibDecimalFloatDeploy.sol) # --offline run the structural half only, and never touch the network. # Lets a test assert the structural half deterministically # instead of depending on whether the registry answered. +# --registry-response +# use the contents of as the registry response instead +# of fetching one, so a test can drive the registry half +# deterministically. Stands in for a fetch that SUCCEEDED; +# there is no stand-in for one that failed, because that path +# is what every offline CI run already takes. # # Consumed by LibDecimalFloatDeployTaggedConstants.t.sol via FFI. Output is one # of: -# OK - every half that ran, passed -# MISSING: - one or more expected constants are absent -# SKIP: - default mode only: the structural half passed but the -# registry was unreachable, so that half did not run +# OK - every half that ran, passed +# MISSING: - one or more expected constants are absent +# SKIP: - default mode only: the structural half passed but +# the registry could not be fetched, so that half +# did not run +# UNREADABLE: - the registry answered and no version could be read +# out of the answer. A failure, not a skip: see the +# registry half below for why. # # Always exits 0 so the test sees the message rather than an ffi failure. @@ -37,6 +48,7 @@ set -uo pipefail lib="src/lib/deploy/LibDecimalFloatDeploy.sol" offline=0 +registry_response="" while [ "$#" -gt 0 ]; do case "$1" in @@ -52,6 +64,18 @@ while [ "$#" -gt 0 ]; do offline=1 shift ;; + --registry-response) + registry_response="${2:-}" + if [ -z "$registry_response" ]; then + printf 'MISSING: --registry-response requires a path' + exit 0 + fi + if [ ! -f "$registry_response" ]; then + printf 'MISSING: no such registry response file %s' "$registry_response" + exit 0 + fi + shift 2 + ;; *) printf 'MISSING: unknown argument %s' "$1" exit 0 @@ -64,6 +88,11 @@ if [ ! -f "$lib" ]; then exit 0 fi +if [ "$offline" -eq 1 ] && [ -n "$registry_response" ]; then + printf 'MISSING: --offline and --registry-response are mutually exclusive' + exit 0 +fi + # The deploy constants that must be pinned for every version. bases="ZOLTU_DEPLOYED_LOG_TABLES_ADDRESS \ LOG_TABLES_DATA_CONTRACT_HASH \ @@ -94,13 +123,45 @@ pinned_suffixes=$( check_suffixes "$pinned_suffixes" # 2. Registry half. +# +# Fetching and reading are kept apart on purpose. Collapsing them makes an +# answer nobody can read look exactly like no answer at all, and "no answer" is +# the branch that reports SKIP — so a change in the registry's response shape +# would silently disable this half forever while every run stayed green. +# +# A fetch that fails is a SKIP: the endpoint 404s for a project with no +# published revisions, which is the state this repo is in until the first +# `sol-v*` tag publishes, and a network that is simply down is not a finding. +# +# A fetch that SUCCEEDS and yields no version is UNREADABLE, and that is a +# failure. This endpoint only answers 2xx for a project that exists, and a +# project exists on the registry because it has revisions; so a readable 2xx +# always carries at least one `"version"`. Zero of them means the response no +# longer looks the way this script reads it. versions="" +registry_answered=0 if [ "$offline" -eq 0 ]; then - versions=$( + if [ -n "$registry_response" ]; then + payload=$(cat "$registry_response") + registry_answered=1 + elif payload=$( curl -fsS --connect-timeout 5 --max-time 20 --retry 2 --retry-delay 1 \ - "https://api.soldeer.xyz/api/v1/revision?project_name=rain-math-float-deploy" 2>/dev/null \ - | grep -oE '"version":"[0-9][0-9.]*"' | cut -d'"' -f4 | sort -u - ) + "https://api.soldeer.xyz/api/v1/revision?project_name=rain-math-float-deploy" 2>/dev/null + ); then + registry_answered=1 + fi + + if [ "$registry_answered" -eq 1 ]; then + # Tolerant of whitespace around the colon so that a pretty-printed response + # reads as a response rather than as an unreadable one. + versions=$( + printf '%s' "$payload" \ + | grep -oE '"version"[[:space:]]*:[[:space:]]*"[0-9][0-9.]*"' \ + | sed -E 's/.*"([0-9][0-9.]*)"$/\1/' \ + | sort -u + ) + fi + if [ -n "$versions" ]; then check_suffixes "$(printf '%s' "$versions" | tr . _)" fi @@ -113,7 +174,9 @@ if [ -n "$missing" ]; then printf '%s' "$missing" | tr ' ' '\n' | grep -v '^$' | sort -u | while IFS= read -r n; do printf ' %s' "$n" done -elif [ "$offline" -eq 0 ] && [ -z "$versions" ]; then +elif [ "$offline" -eq 0 ] && [ "$registry_answered" -eq 1 ] && [ -z "$versions" ]; then + printf 'UNREADABLE: the soldeer registry answered but no version could be read from the response; the registry half did not run' +elif [ "$offline" -eq 0 ] && [ "$registry_answered" -eq 0 ]; then printf 'SKIP: could not fetch published soldeer versions; pinned constant suites are structurally complete' else printf 'OK' diff --git a/test/fixtures/registry-response-pretty-printed.txt b/test/fixtures/registry-response-pretty-printed.txt new file mode 100644 index 0000000..ea51265 --- /dev/null +++ b/test/fixtures/registry-response-pretty-printed.txt @@ -0,0 +1,35 @@ +# SPDX-License-Identifier: LicenseRef-DCL-1.0 +# SPDX-FileCopyrightText: Copyright (c) 2020 Rain Open Source Software Ltd +# +# Fixture for LibDecimalFloatDeployTaggedConstantsTest. The same single +# published version as registry-response-unpinned-version.txt, laid out with +# indentation and a space after each colon. +# +# Whitespace inside JSON carries no meaning, so both spellings say the same +# thing and script/check-published-deploy-constants.sh must read the same +# version out of either. A version scan that only matched the compact spelling +# would read this one as carrying no versions at all, which is the shape the +# script treats as a failure - so this fixture is what keeps a purely cosmetic +# change at the registry from being reported as a broken response. +# +# Deliberately NOT a .json file: this is grep fodder, and the SPDX header above +# is not JSON. The script only ever matches the `"version"` key, so the +# surrounding prose is inert. + +{ + "data": [ + { + "created_at": "2026-08-21T09:09:09.999999Z", + "deleted": false, + "downloads": 999, + "file_size": 99999, + "id": "99999999-9999-4999-8999-999999999999", + "internal_name": "rain-math-float-deploy/9_9_9_21-08-2026_09:09:09_rain-math-float-deploy-9.9.zip", + "private": false, + "project_id": "88888888-8888-4888-8888-888888888888", + "uploader": "77777777-7777-4777-8777-777777777777", + "url": "https://soldeer-revisions.s3.amazonaws.com/rain-math-float-deploy/9_9_9_21-08-2026_09:09:09_rain-math-float-deploy-9.9.zip", + "version": "9.9.9" + } + ] +} diff --git a/test/fixtures/registry-response-unpinned-version.txt b/test/fixtures/registry-response-unpinned-version.txt new file mode 100644 index 0000000..16e0774 --- /dev/null +++ b/test/fixtures/registry-response-unpinned-version.txt @@ -0,0 +1,20 @@ +# SPDX-License-Identifier: LicenseRef-DCL-1.0 +# SPDX-FileCopyrightText: Copyright (c) 2020 Rain Open Source Software Ltd +# +# Fixture for LibDecimalFloatDeployTaggedConstantsTest. Stands in for the +# api.soldeer.xyz response that +# script/check-published-deploy-constants.sh reads, fed to it with +# --registry-response. Shaped like the live endpoint's answer for a project that +# has revisions: a `data` array of revision records, compact, no spaces around +# the colons. +# +# It publishes 9.9.9, a version LibDecimalFloatDeploy pins nothing for, so the +# registry half must report all four of that version's constants absent. The +# other fields carry digits of their own, so a scan that reads any number it +# finds rather than the version key reports the wrong thing. +# +# Deliberately NOT a .json file: this is grep fodder, and the SPDX header above +# is not JSON. The script only ever matches the `"version"` key, so the +# surrounding prose is inert. + +{"data":[{"created_at":"2026-08-21T09:09:09.999999Z","deleted":false,"downloads":999,"file_size":99999,"id":"99999999-9999-4999-8999-999999999999","internal_name":"rain-math-float-deploy/9_9_9_21-08-2026_09:09:09_rain-math-float-deploy-9.9.zip","private":false,"project_id":"88888888-8888-4888-8888-888888888888","uploader":"77777777-7777-4777-8777-777777777777","url":"https://soldeer-revisions.s3.amazonaws.com/rain-math-float-deploy/9_9_9_21-08-2026_09:09:09_rain-math-float-deploy-9.9.zip","version":"9.9.9"}]} diff --git a/test/fixtures/registry-response-unreadable.txt b/test/fixtures/registry-response-unreadable.txt new file mode 100644 index 0000000..6b84de5 --- /dev/null +++ b/test/fixtures/registry-response-unreadable.txt @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: LicenseRef-DCL-1.0 +# SPDX-FileCopyrightText: Copyright (c) 2020 Rain Open Source Software Ltd +# +# Fixture for LibDecimalFloatDeployTaggedConstantsTest. A registry answer that +# arrived intact and that script/check-published-deploy-constants.sh cannot read +# a version out of: every revision record names its version under +# `revision_number` instead, which is what a rename of that field at the +# registry would look like. +# +# The endpoint answers 404 for a project with no published revisions, so a +# response that arrives at all describes a project that has revisions and must +# name their versions somewhere. Reading none out of it therefore means the +# response no longer looks the way the script reads it, and the script must +# report UNREADABLE rather than SKIP: SKIP retires the registry half, and +# retiring it on a response shape change is how the half would go quiet +# permanently while every run stayed green. +# +# Deliberately NOT a .json file: this is grep fodder, and the SPDX header above +# is not JSON. The script only ever matches the `"version"` key, so the +# surrounding prose is inert. + +{"data":[{"created_at":"2026-08-21T09:09:09.999999Z","deleted":false,"downloads":999,"file_size":99999,"id":"99999999-9999-4999-8999-999999999999","internal_name":"rain-math-float-deploy/9_9_9_21-08-2026_09:09:09_rain-math-float-deploy-9.9.zip","private":false,"project_id":"88888888-8888-4888-8888-888888888888","uploader":"77777777-7777-4777-8777-777777777777","url":"https://soldeer-revisions.s3.amazonaws.com/rain-math-float-deploy/9_9_9_21-08-2026_09:09:09_rain-math-float-deploy-9.9.zip","revision_number":"9.9.9"}]} diff --git a/test/src/lib/deploy/LibDecimalFloatDeployTaggedConstants.t.sol b/test/src/lib/deploy/LibDecimalFloatDeployTaggedConstants.t.sol index 63f98d1..e27a3c2 100644 --- a/test/src/lib/deploy/LibDecimalFloatDeployTaggedConstants.t.sol +++ b/test/src/lib/deploy/LibDecimalFloatDeployTaggedConstants.t.sol @@ -17,9 +17,24 @@ import {console2} from "forge-std-1.16.2/src/console2.sol"; /// pinned — needs api.soldeer.xyz). The structural half is asserted /// unconditionally here, so a run that cannot reach the registry still verifies /// something real rather than verifying nothing. +/// +/// The registry half is asserted unconditionally too, by handing the script a +/// fixture in place of a fetched response. That keeps three things off the +/// network: that a published version with no pinned suite is reported, that the +/// version scan reads a response whatever whitespace it carries, and that a +/// response no version can be read out of is a failure rather than the skip an +/// unreachable registry earns. contract LibDecimalFloatDeployTaggedConstantsTest is Test { string constant SCRIPT = "script/check-published-deploy-constants.sh"; string constant HALF_PINNED_FIXTURE = "test/fixtures/half-pinned-deploy-constants.txt"; + string constant UNPINNED_VERSION_RESPONSE_FIXTURE = "test/fixtures/registry-response-unpinned-version.txt"; + string constant PRETTY_PRINTED_RESPONSE_FIXTURE = "test/fixtures/registry-response-pretty-printed.txt"; + string constant UNREADABLE_RESPONSE_FIXTURE = "test/fixtures/registry-response-unreadable.txt"; + + /// Every readable fixture response publishes 9.9.9, a version + /// `LibDecimalFloatDeploy` pins nothing for, so the whole suite is absent. + string constant UNPINNED_9_9_9 = + "MISSING: DECIMAL_FLOAT_CONTRACT_HASH_9_9_9 LOG_TABLES_DATA_CONTRACT_HASH_9_9_9 ZOLTU_DEPLOYED_DECIMAL_FLOAT_ADDRESS_9_9_9 ZOLTU_DEPLOYED_LOG_TABLES_ADDRESS_9_9_9"; /// Structural half against the committed lib. No network, so this asserts /// on every run: a version pinned halfway fails here. @@ -73,6 +88,54 @@ contract LibDecimalFloatDeployTaggedConstantsTest is Test { assertEq(string(out), "OK", "a published soldeer tag is missing pinned deploy constants"); } + /// A version published to the registry with none of its deploy constants + /// pinned must be reported by name. That is the registry half's whole + /// point, and it is the assertion that only ran when api.soldeer.xyz + /// answered until this test drove it from a fixture. + function testRegistryCheckReportsAPublishedVersionWithNoPinnedConstants() external { + assertEq( + _checkAgainstRegistryResponse(UNPINNED_VERSION_RESPONSE_FIXTURE), + UNPINNED_9_9_9, + "the registry check failed to report a published version with no pinned constants" + ); + } + + /// Whitespace inside the response carries no meaning, so a pretty-printed + /// answer must read exactly like a compact one. A scan that only matched + /// the compact spelling would read no versions out of a perfectly good + /// response and condemn it as unreadable. + function testRegistryCheckReadsAPrettyPrintedResponse() external { + assertEq( + _checkAgainstRegistryResponse(PRETTY_PRINTED_RESPONSE_FIXTURE), + UNPINNED_9_9_9, + "the registry check could not read a pretty-printed response" + ); + } + + /// A response that arrives and carries no readable version is a failure, + /// not a skip. Calling it a skip retires the registry half the moment the + /// response shape changes, and every run stays green while that half checks + /// nothing. + function testRegistryCheckFailsOnAResponseWithNoReadableVersion() external { + assertEq( + _checkAgainstRegistryResponse(UNREADABLE_RESPONSE_FIXTURE), + "UNREADABLE: the soldeer registry answered but no version could be read from the response; the registry half did not run", + "an unreadable registry response was not reported as a failure" + ); + } + + /// Both halves against the committed lib, with `fixture` standing in for a + /// fetched registry response so the registry half never touches the + /// network. + function _checkAgainstRegistryResponse(string memory fixture) private returns (string memory) { + string[] memory cmd = new string[](4); + cmd[0] = "bash"; + cmd[1] = SCRIPT; + cmd[2] = "--registry-response"; + cmd[3] = fixture; + return string(vm.ffi(cmd)); + } + function _startsWith(bytes memory s, bytes memory prefix) private pure returns (bool) { if (s.length < prefix.length) { return false;