test(schema): replay the updater from every version a server can hold - #1342
Merged
Conversation
CI has only ever exercised a FRESH INSTALL of the schema. That is not a gap anyone chose; it is what the code makes easy. SchemaUpdaterPage::update() slices its step array from self::$mySchema, which is 0 on a new database, so a fresh install really does run every step from the beginning -- and tests/schema-executes.test.php sets $mySchema to 0 and FOG_SCHEMA to PHP_INT_MAX, which neutralises the two expressions an upgrade turns on. So the SQL side of an upgrade was already covered: a server at version N runs steps [0,N) then [N,end), the same statements in the same order a fresh install runs. What was not covered is the arithmetic and the version it leaves behind, and that is what #1338 broke -- seven statements appended inside the previous step's array left the element count unmoved while FOG_SCHEMA went 360 -> 367, so every existing server sat on ?node=schema permanently, looping a Generic Error toast, while all seven CI checks stayed green because fresh installs were correct throughout. tests/schema-upgrade-replay.test.php closes it. It builds the real step array and, for every version a server could be sitting on, runs the updater's own expressions over it and requires the stored version to land on FOG_SCHEMA. It also pins the two properties that make that arithmetic mean anything: count($this->schema) == FOG_SCHEMA, and keys that are a contiguous list from zero -- the stored version is written as $version + 1 from the array KEY. Needs no database, so it runs on the plain PHP matrix and BLOCKS, rather than riding the schema job where a failure would be one more tolerated leg. No CI change is needed at all: run-all.sh globs tests/*.test.php. Mutation-verified in three directions, restoring from copies rather than git checkout: - #1338 reproduced exactly (the seven steps merged back into step 360, FOG_SCHEMA left at 367) -> "FOG_SCHEMA is 367 but commons/schema.php holds 360 step(s)", plus all 367 starting versions reported stuck. - FOG_SCHEMA lowered below the count -> the stranded-step failure. - a $this->schema[400] = index assignment -> the contiguity failure, which the count check alone does not see. Extracts the shim that loads commons/schema.php outside the application into tests/lib/fog-schema-collector.php. It was carried by schema-executes alone; there are two callers now and there must stay one copy, because two hand-kept descriptions of the schema drifting apart is the failure both tests exist to catch. schema-executes' behaviour is unchanged -- byte- identical output before and after, verified against MariaDB 11.8. schema-gate keeps counting `// N` labels and is not replaced. The two fail on different things: a real count is label-independent and catches shapes the text cannot see, while the label checks cover hygiene a count cannot -- an unlabelled append, or a label with no append. Its docblock recorded that a real count "was tried and rejected" because schema.php wants ~35 constants and a couple of core classes; the collector discovers both rather than listing them, so that objection is answered rather than overruled, and the docblock now says so. Also repoints a reference in commons/schema.php that #1340 left dangling at a test file that was never committed. Co-Authored-By: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this closes
CI has only ever exercised a fresh install of the schema, and that is not a gap anyone chose — it is what the code makes easy.
SchemaUpdaterPage::update()slices its step array fromself::$mySchema, which is0on a new database, so a fresh install really does run every step from the beginning.tests/schema-executes.test.phptherefore sets$mySchema = 0andFOG_SCHEMA = PHP_INT_MAX, which neutralises the two expressions an upgrade turns on:So the SQL side of an upgrade was already covered — a server at version N runs steps
[0,N)then[N,end), which is the same statements in the same order a fresh install runs, andschema-executesruns exactly that against three database engines. What was never covered is the arithmetic and the version it leaves behind.That is what #1338 broke. Seven statements appended inside the previous step's array left the element count unmoved while
FOG_SCHEMAwent 360 → 367. Every existing server was then pinned on?node=schema— the updater had nothing to apply, never advanced the stored version, and answered 204, which jQuery reports asstatusText: "nocontent"and$.notifyFromAPIrenders as a looping Generic Error toast. All seven CI checks were green, because a fresh install was correct throughout.What is added
tests/schema-upgrade-replay.test.phpbuilds the real step array and, for every version a server could be sitting on, runs the updater's own expressions over it and requires the stored version to land onFOG_SCHEMA. It also pins the two properties that make that arithmetic mean anything:count($this->schema) === FOG_SCHEMA— the real element count, not a proxy$version + 1from the array keyNo CI change is needed.
run-all.shglobstests/*.test.php, so it runs in the existing blockingtests (PHP 7.4)/tests (PHP 8.3)jobs. It needs no database on purpose: the upgrade-specific surface is arithmetic, so it blocks rather than riding the schema job where a failure would be one more tolerated leg.Mutation-verified, three directions
Restored from copies rather than
git checkout --.FOG_SCHEMAleft at 367FAIL: FOG_SCHEMA is 367 but commons/schema.php holds 360 step(s), plus all 367 starting versions reported stuckFOG_SCHEMAlowered below the count$this->schema[400] = [...]index assignmentSuite: 145 passed, 0 failed. Verified on PHP 8.3 locally and PHP 7.4 in a container (identical 367-step result), and
schema-executesre-verified against MariaDB 11.8 with byte-identical output before and after the refactor.Refactor
The shim that loads
commons/schema.phpoutside the application moves totests/lib/fog-schema-collector.php. It was carried byschema-executesalone; there are two callers now and there must stay one copy, because two hand-kept descriptions of the schema drifting apart is the failure both tests exist to catch.schema-gateis kept, not replacedThe two fail on different things and neither subsumes the other. A real count is label-independent and catches shapes the text cannot see; the label checks cover hygiene a count cannot — an unlabelled append, or a label with no append.
schema-gate's docblock recorded that a real count "was tried and rejected" becauseschema.phpwants ~35 constants and a couple of core classes; the collector discovers both rather than listing them, so that objection is answered rather than overruled, and the docblock now says so.Also repoints a reference in
commons/schema.phpthat #1340 left dangling at a test file that was never committed.🤖 Generated with Claude Code
https://claude.ai/code/session_01XqpPXAk7bEm8huH9WkiGk6