Files
php-qml/framework/php/tests/snapshot/run.sh

83 lines
3.7 KiB
Bash
Raw Normal View History

Phase 3 sub-commit 5: maker-output snapshot test + phase closure framework/php/tests/snapshot/ holds reference output for every shipped maker (resource Todo, command MarkAllDone, window Todo). The run.sh script: - git-archives the skeleton into a temp dir - composer-installs against the bundle's real path - removes the existing maker outputs so the regenerators don't bail - runs the three makers - diffs each generated file against the matching baseline CI / make quality fail on any drift; if a template change is intended, the baselines must be regenerated in the same commit. Wired into: - framework/skeleton/Makefile's `quality` target (local/dev runs) - .gitea/workflows/ci.yml (CI runs after qmllint) Plus a few hardenings discovered while wiring this up: - The resource maker template now injects NormalizerInterface (not SerializerInterface — that interface lacks ::normalize()). All Todo controllers re-rendered to match. - The command maker template emits a $this->em->flush() so the injected EntityManager isn't a property.onlyWritten violation in PHPStan after the user fills in the body. - phpstan.neon and php-cs-fixer's Finder both exclude tests/snapshot so the baselines aren't auto-rewritten or analysed as live code. CI workflow now also installs FrankenPHP, builds the todo example, and runs the bridge-integration test from Phase 3 sub-commit 4. Phase 3 done. Outstanding follow-ups (deferred per spec): the qmltestrunner-driven QML unit tests, make:bridge:event, make:bridge:read-model, ReactiveObject pagination. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 16:03:41 +02:00
#!/usr/bin/env bash
# Maker-output snapshot test.
# Runs the three Phase-2/Phase-3 makers in a clean temp app and diffs
# the output against the baselines in this directory. Detects silent
# generator drift: any change to a maker template requires the
# corresponding baseline to be updated in the same commit.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../../../.." && pwd)"
BUNDLE="$PROJECT_ROOT/framework/php"
WORK="$(mktemp -d)"
trap 'rm -rf "$WORK"' EXIT INT TERM
# Mirror the skeleton's tracked files into the temp dir.
git -C "$PROJECT_ROOT" archive HEAD framework/skeleton/ | tar -xf - -C "$WORK"
APP="$WORK/framework/skeleton"
# Point the test app's path repo at the actual bundle dir (the
# default '../../php' would resolve relative to /tmp).
sed -i "s|\"../../php\"|\"$BUNDLE\"|" "$APP/symfony/composer.json"
rm -f "$APP/symfony/composer.lock"
( cd "$APP/symfony" && composer install --no-interaction --quiet )
fail=0
check() {
local generated="$1"
local baseline="$2"
if ! diff -q "$generated" "$baseline" >/dev/null 2>&1; then
echo "✗ snapshot mismatch: $(basename "$baseline")" >&2
diff -u "$baseline" "$generated" >&2 || true
fail=1
else
echo "$(basename "$baseline")"
fi
}
v0.2.0 (4/N): make:bridge:resource --with-dto + symfony/validator Closes the input-validation gap that was the audit's headline finding. The legacy generated controller's `if (isset($data['title']))…` body accepted any JSON: empty title slipped through, malformed JSON got swallowed by `?? []`, wrong types were silently coerced via casts. The --with-dto flag generates: - src/Dto/Create<Name>Dto.php — readonly DTO with #[Assert\NotBlank] on title and #[Assert\Length(max: 255)] - src/Dto/Update<Name>Dto.php — same DTO with all fields nullable so PATCH callers send only what changed - src/Controller/<Name>Controller.php — same shape as the legacy controller but actions dispatch via #[MapRequestPayload] Validation failures (missing required field, wrong type, malformed JSON, oversize string) become RFC 7807 application/problem+json automatically — Symfony's RequestPayloadValueResolver does the work. No `if-isset` boilerplate, no silent coercion. Behaviour: - --with-dto is opt-in; legacy template still ships unchanged - audit suggests flipping to default-on once stable; that's a follow-up - maker fails loud (composer require hint) if symfony/validator isn't autoloadable - skeleton + example/todo composer.json pull symfony/validator so scaffolded apps work out of the box Snapshot test exercises both modes (legacy + --with-dto). New baselines TodoControllerWithDto.php / CreateTodoDto.php / UpdateTodoDto.php under tests/snapshot/. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 20:10:52 +02:00
clear_outputs() {
rm -f "$APP/symfony/src/Entity/Todo.php"
rm -f "$APP/symfony/src/Controller/TodoController.php"
rm -f "$APP/symfony/src/Dto/CreateTodoDto.php"
rm -f "$APP/symfony/src/Dto/UpdateTodoDto.php"
rm -f "$APP/qml/TodoList.qml"
}
# ── Mode 1: legacy (no --with-dto) ────────────────────────────────────
clear_outputs
( cd "$APP/symfony" \
&& bin/console make:bridge:resource Todo --no-interaction >/dev/null \
&& bin/console make:bridge:command MarkAllDone --no-interaction >/dev/null \
&& bin/console make:bridge:window Todo --no-interaction >/dev/null \
&& bin/console make:bridge:event TodoCompleted --no-interaction >/dev/null )
v0.2.0 (4/N): make:bridge:resource --with-dto + symfony/validator Closes the input-validation gap that was the audit's headline finding. The legacy generated controller's `if (isset($data['title']))…` body accepted any JSON: empty title slipped through, malformed JSON got swallowed by `?? []`, wrong types were silently coerced via casts. The --with-dto flag generates: - src/Dto/Create<Name>Dto.php — readonly DTO with #[Assert\NotBlank] on title and #[Assert\Length(max: 255)] - src/Dto/Update<Name>Dto.php — same DTO with all fields nullable so PATCH callers send only what changed - src/Controller/<Name>Controller.php — same shape as the legacy controller but actions dispatch via #[MapRequestPayload] Validation failures (missing required field, wrong type, malformed JSON, oversize string) become RFC 7807 application/problem+json automatically — Symfony's RequestPayloadValueResolver does the work. No `if-isset` boilerplate, no silent coercion. Behaviour: - --with-dto is opt-in; legacy template still ships unchanged - audit suggests flipping to default-on once stable; that's a follow-up - maker fails loud (composer require hint) if symfony/validator isn't autoloadable - skeleton + example/todo composer.json pull symfony/validator so scaffolded apps work out of the box Snapshot test exercises both modes (legacy + --with-dto). New baselines TodoControllerWithDto.php / CreateTodoDto.php / UpdateTodoDto.php under tests/snapshot/. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 20:10:52 +02:00
check "$APP/symfony/src/Entity/Todo.php" "$SCRIPT_DIR/Todo.php"
check "$APP/symfony/src/Controller/TodoController.php" "$SCRIPT_DIR/TodoController.php"
check "$APP/qml/TodoList.qml" "$SCRIPT_DIR/TodoList.qml"
check "$APP/symfony/src/Controller/MarkAllDoneController.php" "$SCRIPT_DIR/MarkAllDoneController.php"
check "$APP/qml/TodoWindow.qml" "$SCRIPT_DIR/TodoWindow.qml"
check "$APP/symfony/src/Event/TodoCompletedEvent.php" "$SCRIPT_DIR/TodoCompletedEvent.php"
check "$APP/symfony/src/EventSubscriber/TodoCompletedSubscriber.php" "$SCRIPT_DIR/TodoCompletedSubscriber.php"
check "$APP/qml/TodoCompletedEventHandler.qml" "$SCRIPT_DIR/TodoCompletedEventHandler.qml"
v0.2.0 (4/N): make:bridge:resource --with-dto + symfony/validator Closes the input-validation gap that was the audit's headline finding. The legacy generated controller's `if (isset($data['title']))…` body accepted any JSON: empty title slipped through, malformed JSON got swallowed by `?? []`, wrong types were silently coerced via casts. The --with-dto flag generates: - src/Dto/Create<Name>Dto.php — readonly DTO with #[Assert\NotBlank] on title and #[Assert\Length(max: 255)] - src/Dto/Update<Name>Dto.php — same DTO with all fields nullable so PATCH callers send only what changed - src/Controller/<Name>Controller.php — same shape as the legacy controller but actions dispatch via #[MapRequestPayload] Validation failures (missing required field, wrong type, malformed JSON, oversize string) become RFC 7807 application/problem+json automatically — Symfony's RequestPayloadValueResolver does the work. No `if-isset` boilerplate, no silent coercion. Behaviour: - --with-dto is opt-in; legacy template still ships unchanged - audit suggests flipping to default-on once stable; that's a follow-up - maker fails loud (composer require hint) if symfony/validator isn't autoloadable - skeleton + example/todo composer.json pull symfony/validator so scaffolded apps work out of the box Snapshot test exercises both modes (legacy + --with-dto). New baselines TodoControllerWithDto.php / CreateTodoDto.php / UpdateTodoDto.php under tests/snapshot/. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 20:10:52 +02:00
# ── Mode 2: --with-dto (re-runs make:bridge:resource only) ────────────
# The entity + QML output is byte-identical between modes; only the
# controller swaps and the two DTOs appear. Re-checking the unchanged
# outputs would just be noise.
clear_outputs
( cd "$APP/symfony" \
&& bin/console make:bridge:resource Todo --with-dto --no-interaction >/dev/null )
check "$APP/symfony/src/Controller/TodoController.php" "$SCRIPT_DIR/TodoControllerWithDto.php"
check "$APP/symfony/src/Dto/CreateTodoDto.php" "$SCRIPT_DIR/CreateTodoDto.php"
check "$APP/symfony/src/Dto/UpdateTodoDto.php" "$SCRIPT_DIR/UpdateTodoDto.php"
Phase 3 sub-commit 5: maker-output snapshot test + phase closure framework/php/tests/snapshot/ holds reference output for every shipped maker (resource Todo, command MarkAllDone, window Todo). The run.sh script: - git-archives the skeleton into a temp dir - composer-installs against the bundle's real path - removes the existing maker outputs so the regenerators don't bail - runs the three makers - diffs each generated file against the matching baseline CI / make quality fail on any drift; if a template change is intended, the baselines must be regenerated in the same commit. Wired into: - framework/skeleton/Makefile's `quality` target (local/dev runs) - .gitea/workflows/ci.yml (CI runs after qmllint) Plus a few hardenings discovered while wiring this up: - The resource maker template now injects NormalizerInterface (not SerializerInterface — that interface lacks ::normalize()). All Todo controllers re-rendered to match. - The command maker template emits a $this->em->flush() so the injected EntityManager isn't a property.onlyWritten violation in PHPStan after the user fills in the body. - phpstan.neon and php-cs-fixer's Finder both exclude tests/snapshot so the baselines aren't auto-rewritten or analysed as live code. CI workflow now also installs FrankenPHP, builds the todo example, and runs the bridge-integration test from Phase 3 sub-commit 4. Phase 3 done. Outstanding follow-ups (deferred per spec): the qmltestrunner-driven QML unit tests, make:bridge:event, make:bridge:read-model, ReactiveObject pagination. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 16:03:41 +02:00
if [ "$fail" -ne 0 ]; then
echo "Snapshot test failed. If the change is intended, update the baselines under $SCRIPT_DIR/." >&2
exit 1
fi
v0.2.0 (4/N): make:bridge:resource --with-dto + symfony/validator Closes the input-validation gap that was the audit's headline finding. The legacy generated controller's `if (isset($data['title']))…` body accepted any JSON: empty title slipped through, malformed JSON got swallowed by `?? []`, wrong types were silently coerced via casts. The --with-dto flag generates: - src/Dto/Create<Name>Dto.php — readonly DTO with #[Assert\NotBlank] on title and #[Assert\Length(max: 255)] - src/Dto/Update<Name>Dto.php — same DTO with all fields nullable so PATCH callers send only what changed - src/Controller/<Name>Controller.php — same shape as the legacy controller but actions dispatch via #[MapRequestPayload] Validation failures (missing required field, wrong type, malformed JSON, oversize string) become RFC 7807 application/problem+json automatically — Symfony's RequestPayloadValueResolver does the work. No `if-isset` boilerplate, no silent coercion. Behaviour: - --with-dto is opt-in; legacy template still ships unchanged - audit suggests flipping to default-on once stable; that's a follow-up - maker fails loud (composer require hint) if symfony/validator isn't autoloadable - skeleton + example/todo composer.json pull symfony/validator so scaffolded apps work out of the box Snapshot test exercises both modes (legacy + --with-dto). New baselines TodoControllerWithDto.php / CreateTodoDto.php / UpdateTodoDto.php under tests/snapshot/. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 20:10:52 +02:00
echo "All maker outputs match snapshots (legacy + --with-dto modes)."