diff --git a/.gitattributes b/.gitattributes index 176a458..20a1962 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,2 @@ * text=auto +/tests/fixtures/*.bundle binary diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 28cea4c..7486bbd 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -39,6 +39,35 @@ $ vendor/bin/phpunit * A script `test-git-versions.sh` is available in repository to test gitlib against many git versions. * The tests will be automatically run by [GitHub Actions](https://github.com/features/actions) against pull requests. +* Tests run fully offline: no network access is required. + +## Test fixtures + +Most tests run against a fixture repository cloned from `tests/fixtures/foobar.bundle`, +a local git bundle. Using a bundle instead of a network clone keeps the tests fast and +fully offline. + +If you need a new fixture scenario (a specific merge, encoding, or signed-commit shape, +for example), regenerate the bundle locally, entirely from the one already in the repo: + +```bash +$ git clone tests/fixtures/foobar.bundle /tmp/foobar-fixture && cd /tmp/foobar-fixture +$ for b in $(git branch -r | grep -v HEAD | sed 's#origin/##'); do +$ git branch --track "$b" "origin/$b" +$ done +# ... add your commits, branches or tags ... +$ git bundle create foobar.bundle \ + HEAD refs/heads/master refs/heads/new-feature refs/heads/diff-features \ + refs/heads/pagination refs/heads/path-resolving refs/tags/0.1 refs/tags/annotated +$ cp foobar.bundle /path/to/gitlib/tests/fixtures/foobar.bundle +``` + +Then update the commit SHA constants in `AbstractTestCase` to match, and run +`tests/fixtures/verify-bundle.sh`. It checks the bundle's integrity, its ref list against +an allow-list, and its size, since GitHub renders any change to this binary file as an +opaque diff. If your change intentionally adds a ref or grows the file, update +`ALLOWED_REFS` or `MAX_SIZE_KB` in that script as part of the same pull request, so the +reason for the change is explicit and reviewable rather than a silent binary diff. ## Standard code diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index ae4f7d7..73634aa 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -6,6 +6,16 @@ on: pull_request: jobs: + verify-fixture: + name: Verify Test Fixture Bundle + runs-on: ubuntu-24.04 + steps: + - name: Checkout Code + uses: actions/checkout@v7 + + - name: Verify fixture bundle integrity, refs and size + run: tests/fixtures/verify-bundle.sh + check-cs: name: Check Coding Standards runs-on: ubuntu-24.04 diff --git a/tests/Gitonomy/Git/Tests/AbstractTestCase.php b/tests/Gitonomy/Git/Tests/AbstractTestCase.php index e56bc35..80cd16e 100644 --- a/tests/Gitonomy/Git/Tests/AbstractTestCase.php +++ b/tests/Gitonomy/Git/Tests/AbstractTestCase.php @@ -18,7 +18,11 @@ abstract class AbstractTestCase extends TestCase { - public const REPOSITORY_URL = 'https://github.com/gitonomy/foobar.git'; + /** + * Local git bundle used as a fixture repository: cloning from it behaves like + * cloning a remote repository, but requires no network access. + */ + public const REPOSITORY_URL = __DIR__.'/../../../fixtures/foobar.bundle'; public const NO_MESSAGE_COMMIT = '011cd0c1625190d2959ee9a8f9f822006d94b661'; public const LONGFILE_COMMIT = '4f17752acc9b7c54ba679291bf24cb7d354f0f4f'; diff --git a/tests/fixtures/foobar.bundle b/tests/fixtures/foobar.bundle new file mode 100644 index 0000000..bbd71de Binary files /dev/null and b/tests/fixtures/foobar.bundle differ diff --git a/tests/fixtures/verify-bundle.sh b/tests/fixtures/verify-bundle.sh new file mode 100755 index 0000000..f543e21 --- /dev/null +++ b/tests/fixtures/verify-bundle.sh @@ -0,0 +1,53 @@ +#!/bin/bash +# +# Guards tests/fixtures/foobar.bundle against unexpected changes: any pull +# request touching it should be reviewed by hand (see CONTRIBUTING.md), but +# this catches the obvious cases automatically: +# - a corrupted or incomplete bundle +# - refs that were not in the original fixture +# - a file that has silently grown well past its expected size +# +# Run it locally with: tests/fixtures/verify-bundle.sh + +set -euo pipefail + +cd "$(dirname "${BASH_SOURCE[0]}")" > /dev/null + +BUNDLE="foobar.bundle" +MAX_SIZE_KB=200 + +ALLOWED_REFS=" +HEAD +refs/heads/diff-features +refs/heads/master +refs/heads/new-feature +refs/heads/pagination +refs/heads/path-resolving +refs/tags/0.1 +refs/tags/annotated +" + +echo "== Verifying $BUNDLE ==" + +VERIFY_OUTPUT="$(git bundle verify "$BUNDLE")" +echo "$VERIFY_OUTPUT" + +SIZE_KB=$(( $(stat -c%s "$BUNDLE") / 1024 )) +echo "Size: ${SIZE_KB}KB (limit: ${MAX_SIZE_KB}KB)" +if [ "$SIZE_KB" -gt "$MAX_SIZE_KB" ]; then + echo "ERROR: $BUNDLE is ${SIZE_KB}KB, which exceeds the ${MAX_SIZE_KB}KB limit." >&2 + echo "If this growth is expected, bump MAX_SIZE_KB in $0 as part of the same PR." >&2 + exit 1 +fi + +ACTUAL_REFS="$(grep -oE '(refs/[^ ]+|HEAD)$' <<< "$VERIFY_OUTPUT" | sort -u)" +UNEXPECTED_REFS="$(comm -23 <(echo "$ACTUAL_REFS") <(sort -u <<< "$ALLOWED_REFS"))" + +if [ -n "$UNEXPECTED_REFS" ]; then + echo "ERROR: $BUNDLE contains refs that are not in the allow-list:" >&2 + echo "$UNEXPECTED_REFS" >&2 + echo "If this is expected, update ALLOWED_REFS in $0 as part of the same PR." >&2 + exit 1 +fi + +echo "OK: bundle structure and size are within expected bounds."