21 KiB
Git HTTP Smart Protocol Comprehensive Test Suite
ID: bb46
Problem
Production bug discovered: git clone failed because ngit-grasp wasn't decompressing gzip-encoded request bodies that modern git clients send. This reveals a critical gap in testing - we need comprehensive git HTTP smart protocol compliance tests that catch real-world client behavior before production.
Current testing gaps:
- No tests for Content-Encoding variations (gzip, deflate, none)
- No tests across git protocol versions (v0, v1, v2)
- No tests for edge cases (partial clones, large repos, concurrent requests)
- No integration tests with real git clients
- No conformance testing against git's own http-backend behavior
Plan
-
Phase 1: Research existing test suites and specifications
- Find git project's own http-backend tests
- Survey cgit, gitea, gitlab test approaches
- Identify RFC/specification compliance requirements
- Document git HTTP smart protocol edge cases
- Output: Research Report section in this issue
-
Phase 2: Design comprehensive test matrix
- Content-Encoding variations (gzip, deflate, identity, none)
- Git protocol versions (v0, v1, v2)
- Git operations (clone, fetch, push, ls-remote)
- Repository states (empty, large, many refs, shallow)
- Client configurations and versions
- Error conditions (malformed requests, timeouts, auth failures)
- Output:
docs/reference/git-http-test-matrix.md
-
Phase 3: Implementation strategy
- Integration test approach using TestRelay fixture
- Real git client testing framework
- Conformance test suite structure
- CI/CD integration plan
- Output:
docs/explanation/git-integration-test-framework.md
-
Phase 4: Create test implementation in grasp-audit
- Build git protocol utilities in
grasp-audit/src/git/ - Implement priority test cases in
grasp-audit/src/specs/grasp01/ - Add to existing grasp-audit test suite
- Document test coverage
- Output:
grasp-audit/src/git/,grasp-audit/src/specs/grasp01/git_protocol.rs
- Build git protocol utilities in
-
Phase 5: MVP - Single test following existing pattern
- Pick minimal test requiring minimal GitClient wrapper
- Follow pattern from
git_clone.rsand how it's called in ngit-grasp tests - Validate architecture works end-to-end
- Output: Working MVP test demonstrating the pattern
- Completed:
grasp-audit/src/git/client.rs,grasp-audit/src/specs/grasp01/git_http_protocol.rs,tests/git_http_protocol.rs
Vision
Build comprehensive git HTTP smart protocol compliance tests in grasp-audit that:
- Validate GRASP conformance - Ensure all GRASP implementations fully comply with git HTTP protocol
- Catch real-world bugs - Test with actual git client behavior (gzip, protocol versions, edge cases)
- Reusable across implementations - Any GRASP implementation can use these tests for validation
- Extend existing patterns - Build on grasp-audit's existing HTTP testing infrastructure
Architecture
grasp-audit/
├── src/
│ ├── git/ # NEW: Git protocol utilities
│ │ ├── mod.rs
│ │ ├── pkt_line.rs # Pkt-line parsing
│ │ ├── protocol.rs # Protocol v2 support
│ │ └── client.rs # GitClient wrapper (uses relay_domain)
│ │
│ └── specs/grasp01/
│ ├── git_clone.rs # EXISTING: Basic clone tests
│ ├── git_protocol.rs # NEW: Protocol compliance tests
│ ├── git_fetch.rs # NEW: Fetch protocol tests
│ └── git_push.rs # NEW: Push protocol tests
│
ngit-grasp/
└── tests/
└── git_http_protocol.rs # Calls grasp-audit tests (uses TestRelay)
Integration Pattern
grasp-audit provides:
- Protocol utilities (pkt-line parsing, protocol v2)
- Conformance test functions that accept
relay_domainparameter - Reusable across any GRASP implementation
ngit-grasp provides:
- TestRelay fixture for convenience
- Integration tests that call grasp-audit functions
- Implementation-specific tests
MVP Approach
Start with ONE test that:
- Requires minimal GitClient wrapper
- Follows existing
git_clone.rspattern - Demonstrates end-to-end architecture
- Validates the approach before building full suite
Progress
2026-01-21 [Session 18:00] - MVP COMPLETE ✅
- MVP Implementation Complete: All three architect agents completed their work
- Test Selection (Architect 1):
- Selected:
test_gzip_encoded_upload_pack_request - Rationale: Catches production bug, minimal wrapper needed, follows existing pattern
- Uses existing
OwnerStateDataPushedfixture - No new GitClient wrapper needed for MVP - uses reqwest directly
- Selected:
- GitClient Design (Architect 2):
- Created minimal wrapper in
grasp-audit/src/git/client.rs - Methods:
get_info_refs(),upload_pack_gzip(),receive_pack_gzip() - Builder pattern:
GitClient::builder(relay_domain).with_gzip(true).build() - Uses
flate2for gzip compression - Follows existing grasp-audit patterns (relay_domain parameter, reqwest client)
- Created minimal wrapper in
- Implementation (Architect 3):
- Created:
grasp-audit/src/git/mod.rs- Module exports - Created:
grasp-audit/src/git/client.rs- GitClient wrapper (200+ lines) - Created:
grasp-audit/src/specs/grasp01/git_http_protocol.rs- Test file - Created:
tests/git_http_protocol.rs- Integration test in ngit-grasp - Modified:
grasp-audit/Cargo.toml- Added flate2 dependency - Modified:
grasp-audit/src/lib.rs- Exported git module - Modified:
grasp-audit/src/specs/grasp01/mod.rs- Exported GitHttpProtocolTests
- Created:
- Test Verification:
- Test passes:
cargo test --test git_http_protocol test_gzip_encoded_upload_pack - Validates gzip-encoded request handling (production bug scenario)
- Follows existing pattern:
isolated_test!macro, TestRelay fixture
- Test passes:
- Architecture Validated:
- grasp-audit provides test functions accepting
relay_domainparameter - ngit-grasp integration tests use TestRelay and call grasp-audit functions
- Pattern is reusable and extensible
- grasp-audit provides test functions accepting
- Next: Review MVP implementation, then expand to full test suite
2026-01-21 [Session 17:30]
- Updated: Issue with vision, architecture, and MVP approach
- Plan: Build MVP with single test following existing pattern
- Next: Architect to plan MVP test selection and implementation
2026-01-21 [Session 17:15] - REVISED DECISION
- DECISION REVERSED: Tests SHOULD move to grasp-audit
- User clarification: "We want other GRASP implementations to be fully compatible with the git HTTP protocol"
- Key insight: Git HTTP compliance IS part of GRASP conformance, not just implementation detail
- Architect-plan analysis revealed:
- ✅ grasp-audit ALREADY tests HTTP endpoints extensively (CORS, git clone, capabilities)
- ✅ Existing fixture system (
relay_domainparameter) can support comprehensive git HTTP tests - ✅ Pattern already exists:
git_clone.rsmakes direct HTTP requests via reqwest - ✅ Shell git commands already used:
clone_repo(),try_push()in fixtures - ❌ Previous recommendation was too quick to dismiss - architectural barrier is lower than assessed
- Technical findings:
- grasp-audit's
test-ngit-relay.shalready manages relay lifecycle - Tests receive
relay_domainparameter for HTTP access - TestRelay is a convenience, not a requirement
- What's missing: pkt-line parsing, protocol v2 support, comprehensive edge cases
- grasp-audit's
- Proposed architecture:
- Add
grasp-audit/src/git/module for protocol utilities (pkt_line.rs, protocol.rs, client.rs) - Expand existing
specs/grasp01/git_clone.rswith protocol-level validation - Add new test files:
git_fetch.rs,git_push.rs,git_protocol.rs - ngit-grasp keeps TestRelay for convenience, calls grasp-audit library functions
- Add
- Specification clarity needed:
- GRASP-01 spec should explicitly require "Git Smart HTTP protocol as defined in git-scm.com/docs/http-protocol"
- Should require protocol v2 support when client requests it
- Should specify HTTP status codes for error conditions
- Migration path:
- Phase 1: Add git protocol utilities to grasp-audit
- Phase 2: Expand existing tests with protocol-level validation
- Phase 3: ngit-grasp integration tests continue using TestRelay, call grasp-audit functions
- Next: Implement git protocol utilities in grasp-audit, migrate tests
2026-01-21 [Session 16:56]
- Fixed: Worktree setup to follow proper start-work conventions
- Issue: Original worktree was created with branch name
bb46instead ofbb46-git-http-protocol-test-suite - Action: Removed incorrect worktree and recreated with proper naming
- Current state:
- ✅ Branch name:
bb46-git-http-protocol-test-suite(matches issue filename without .md) - ✅ Worktree directory:
worktrees/bb46-git-http-protocol-test-suite - ✅ dcdev-issues symlink: Properly configured and working
- ❌ Test files: Lost during worktree recreation (were untracked)
- ✅ Branch name:
- Next: Test files need to be recreated from previous session's work
tests/git_http_protocol.rs(18,844 bytes - 50 integration tests)tests/common/git_client.rs(15,732 bytes - Git client wrapper)docs/reference/git-http-test-matrix.md(20,169 bytes)docs/explanation/git-integration-test-framework.md(22,329 bytes)
- Note: Files were created in Session 17:30 but not committed before worktree was incorrectly set up
2026-01-21 [Session 16:45] - SUPERSEDED BY SESSION 17:15
- Decision: Keep tests in ngit-grasp, do NOT move to grasp-audit
- Rationale:
- grasp-audit is for GRASP compliance testing (Nostr events, black-box)
- bb46 tests are implementation-specific (HTTP protocol, git client behavior)
- grasp-audit already has sufficient git tests for GRASP-01 compliance
- bb46 tests go beyond spec requirements (protocol versions, gzip, edge cases)
- No other GRASP implementations exist yet to benefit from shared tests
- Architectural analysis:
- grasp-audit scope: Nostr-level compliance via AuditClient
- bb46 tests scope: HTTP-level implementation quality via GitClient
- Scope mismatch would require new abstraction layer and maintenance burden
- Alternative approach: Document test patterns for other implementations to adapt
- Next: Commit tests to ngit-grasp, add reference note to grasp-audit README
2026-01-21 [Session 16:27]
- Started work: Created worktree for issue bb46
- Moved test implementation files from root repository to worktree:
tests/git_http_protocol.rs(18,844 bytes)tests/common/git_client.rs(15,732 bytes)docs/reference/git-http-test-matrix.md(20,169 bytes)docs/explanation/git-integration-test-framework.md(22,329 bytes)
- Files are now in worktree as untracked files, ready to be committed
- Next: Review files, add to git, and commit to bb46 branch
2026-01-21 [Session 17:30]
- Completed: Phase 3 & 4 - Implementation strategy and test framework
- Created:
docs/explanation/git-integration-test-framework.md- Comprehensive design document - Created:
tests/common/git_client.rs- Git client wrapper with protocol/compression control - Created:
tests/git_http_protocol.rs- 50 integration tests covering:- Protocol versions (v0, v1, v2)
- Content encoding (gzip, uncompressed)
- Git operations (clone, fetch, ls-remote, shallow clone)
- Edge cases (nonexistent repos, timeouts, concurrent clones)
- All 50 tests passing
- Framework features:
- Configurable git client wrapper (protocol version, gzip, timeout)
- Proper test isolation using TestRelay fixture
- Helper functions for repo setup with announcement + state events
- Slow tests marked with
#[ignore]for CI optimization
2026-01-21 [Session 16:00]
- Completed: Phase 1 - Research on existing test suites and specifications
- Analyzed: Git project's HTTP test suite (t55*.sh files)
- Reviewed: Gitea, GitLab test approaches
- Key findings from research:
- Git's t5562-http-backend-content-length.sh is the key reference for Content-Encoding tests
- Git supports both "gzip" and "x-gzip" Content-Encoding values
- Protocol v2 is now default in modern git clients
- Git tests use real Apache httpd via lib-httpd.sh
- Documented: 8 categories of edge cases, specification references
- Output: Research Report section added to this issue
2026-01-21 [Session 14:30]
- Completed: Phase 2 - Comprehensive test matrix designed
- Deliverable:
docs/reference/git-http-test-matrix.md - Coverage: 95 test scenarios across 10 categories
- Key findings:
- Content-Encoding: 0% coverage (critical gap - caused production bug)
- Protocol Versions: 0% coverage (v2 is default in modern git)
- Git Operations: ~15% coverage (only basic clone tested)
- Error Handling: ~20% coverage (minimal edge cases)
- Prioritization: 25 P0 tests, 40 P1 tests, 30 P2 tests
- Roadmap: 4-week implementation plan with weekly milestones
- Next: Phase 3 - Implementation strategy and test infrastructure
2026-01-21 [Session Start]
- Created: Issue created to track comprehensive git HTTP protocol testing
- Context: Discovered gzip decompression bug in production
- Goal: Build test suite that catches real-world client issues before production
Notes
- Root cause: Modern git clients send gzip-encoded request bodies by default
- Current tests use TestRelay fixture (tests/common/relay.rs:14)
- Need to test against REAL git clients, not just synthetic requests
- Consider using git's own test suite as reference implementation
- Must cover both happy path AND edge cases
- Integration tests should run against live ngit-grasp instance
Research Report (Phase 1)
1. Git Project's Own Test Suite
Key Test Files (in git/t/):
| File | Purpose |
|---|---|
t5551-http-fetch-smart.sh |
Core smart HTTP fetch tests |
t5541-http-push-smart.sh |
Smart HTTP push tests |
t5555-http-smart-common.sh |
Protocol version tests (v0, v1, v2) |
t5560-http-backend-noserver.sh |
HTTP backend without server (unit-like) |
t5561-http-backend.sh |
Full HTTP backend integration |
t5562-http-backend-content-length.sh |
CRITICAL: Content-Length and gzip tests |
t5539-fetch-http-shallow.sh |
Shallow clone over HTTP |
t5559-http-fetch-smart-http2.sh |
HTTP/2 protocol tests |
t556x_common |
Shared test helpers |
lib-httpd.sh |
Apache httpd test infrastructure |
What Git Tests:
- Reference discovery (info/refs?service=git-upload-pack)
- Smart service responses (git-upload-pack, git-receive-pack)
- Content-Type validation (
application/x-git-upload-pack-advertisement) - Protocol version negotiation (v0, v1, v2 via Git-Protocol header)
- Authentication (Basic auth, credential helpers)
- Redirects (301, 302)
- Chunked transfer encoding
- Large fetch-pack requests
- Cookie handling
- Error conditions (invalid Content-Type, server errors)
2. Content-Encoding Handling (Critical!)
From git's http-backend.c:
const char *encoding = getenv("HTTP_CONTENT_ENCODING");
if (encoding && (!strcmp(encoding, "gzip") || !strcmp(encoding, "x-gzip")))
gzipped_request = 1;
From t5562-http-backend-content-length.sh:
# Tests gzip-encoded request bodies
test_expect_success GZIP 'fetch gzipped' '
test_env HTTP_CONTENT_ENCODING="gzip" test_http_env upload fetch_body.gz &&
verify_http_result "200 OK"
'
test_expect_success GZIP 'push gzipped' '
test_env HTTP_CONTENT_ENCODING="gzip" test_http_env receive push_body.gz &&
verify_http_result "200 OK"
'
MUST TEST:
Content-Encoding: gzip- Most common, git client defaultContent-Encoding: x-gzip- Alternative gzip identifierContent-Encoding: identity- Explicit no encoding- No Content-Encoding header - Implicit identity
- Truncated gzip streams (error handling)
- Empty gzip bodies (error handling)
3. Protocol Versions
Version 0 (Legacy):
- No Git-Protocol header
- Capabilities in first ref line after NUL byte
symref=HEAD:refs/heads/mainin capability line
Version 1:
Git-Protocol: version=1header- Response starts with
version 1pkt-line - Same capability format as v0
Version 2:
Git-Protocol: version=2header- Response starts with
version 2pkt-line - Capabilities as separate lines (agent, ls-refs, fetch, etc.)
- Stateless by default
- Commands: ls-refs, fetch, server-option, object-info
From t5555-http-smart-common.sh:
test_expect_success 'git upload-pack --advertise-refs: v2' '
cat >expect <<-EOF &&
version 2
agent=FAKE
ls-refs=unborn
fetch=shallow wait-for-done
server-option
object-format=$(test_oid algo)
0000
EOF
GIT_PROTOCOL=version=2 git upload-pack --advertise-refs . >out
'
4. HTTP Protocol Specification Requirements
From git-scm.com/docs/http-protocol:
Reference Discovery:
- GET
$GIT_URL/info/refs?service=git-upload-pack - Response Content-Type:
application/x-git-upload-pack-advertisement - Response must start with
# service=git-upload-packpkt-line - Must include capability declarations on first ref
Smart Service (upload-pack):
- POST
$GIT_URL/git-upload-pack - Request Content-Type:
application/x-git-upload-pack-request - Response Content-Type:
application/x-git-upload-pack-result - Cache-Control: no-cache, max-age=0, must-revalidate
Smart Service (receive-pack):
- POST
$GIT_URL/git-receive-pack - Request Content-Type:
application/x-git-receive-pack-request - Response Content-Type:
application/x-git-receive-pack-result
Error Handling:
- 403 Forbidden for disabled services
- 404 Not Found for missing repositories
- Must NOT return 200 OK for non-existent repos
5. Edge Cases That MUST Be Tested
Content-Encoding:
- Gzip-encoded POST bodies (clone, fetch, push)
- x-gzip encoding variant
- Truncated gzip streams
- Empty gzip bodies
- Mixed encoding (gzip request, identity response)
Protocol Negotiation:
- Client requests v2, server supports v2
- Client requests v2, server only supports v0 (fallback)
- Client requests v1
- No version specified (default to v0)
Repository States:
- Empty repository (no refs)
- Repository with many refs (>2000 tags test in git)
- Shallow repository
- Repository with large objects
- Repository with submodules
Operations:
- Clone (full)
- Clone (shallow with --depth)
- Clone (partial with --filter)
- Fetch (incremental)
- Fetch (shallow-since, shallow-exclude)
- Push (standard)
- Push (atomic)
- Push (chunked encoding for large packs)
- ls-remote
HTTP Edge Cases:
- Chunked Transfer-Encoding
- Large Content-Length values
- CONTENT_LENGTH overflow (ssize_t boundary)
- Empty CONTENT_LENGTH
- HTTP/1.0 vs HTTP/1.1
- HTTP/2 (if supported)
- Keep-alive connections
- Redirects (301, 302)
- Path traversal attempts (security)
Authentication:
- No auth (public repo)
- Basic auth required
- Auth only for push
- Auth only for objects (half-auth)
- Invalid credentials
- Expired credentials
6. Other Implementation Approaches
Gitea:
- Uses real git client integration tests
- Tests HTTP clone/push with actual git commands
- Tests LFS integration
- Tests SSH and HTTP in parallel
GitLab:
- Extensive RSpec tests for git HTTP
- Tests authentication edge cases
- Tests project path variations (.git suffix handling)
- Tests blocked users, expired passwords
- Tests namespace/project creation via push
7. Recommendations for Test Approach
Priority 1 - Critical (Would have caught our bug):
- Content-Encoding: gzip for POST bodies
- Content-Encoding: x-gzip variant
- Truncated/malformed gzip handling
Priority 2 - Protocol Compliance:
- Protocol version negotiation (v0, v1, v2)
- Correct Content-Type headers
- Correct pkt-line format
- Capability advertisement
Priority 3 - Operations:
- Clone (basic)
- Clone (shallow)
- Fetch (incremental)
- Push (basic)
- ls-remote
Priority 4 - Edge Cases:
- Empty repository
- Large repository (many refs)
- Chunked encoding
- HTTP/2 (if applicable)
Test Infrastructure:
- Use real git client (not mocked requests)
- Test with multiple git versions if possible
- Use TestRelay fixture for isolation
- Consider adapting git's t5562 test approach
8. Specification References
| Document | URL |
|---|---|
| HTTP Protocol | https://git-scm.com/docs/http-protocol |
| Pack Protocol | https://git-scm.com/docs/pack-protocol |
| Protocol v2 | https://git-scm.com/docs/protocol-v2 |
| Git Test Suite | https://github.com/git/git/tree/master/t |
| RFC 2616 (HTTP/1.1) | https://www.ietf.org/rfc/rfc2616.txt |
| RFC 1952 (GZIP) | https://www.ietf.org/rfc/rfc1952.txt |
9. Key Gotchas from Other Implementations
- Git's gzip handling: Uses
HTTP_CONTENT_ENCODINGenv var, supports both "gzip" and "x-gzip" - Protocol fallback: Client may request v2 but server can respond with v0 - client must handle gracefully
- Half-auth: Some servers require auth only for objects, not refs - complex to test
- Path normalization: Must handle
/repo.gitvs/repovs/repo.git/consistently - Chunked encoding: Large pushes use chunked encoding when http.postbuffer is small
- Empty repos: Must handle repos with no refs (capabilities^{} line)
- Peeled refs: Annotated tags must show both tag and peeled object
- Symref: HEAD symref must be communicated in capabilities