Files
ngit-grasp/bb46-git-http-protocol-test-suite.md
T

512 lines
21 KiB
Markdown

# 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
- [x] 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
- [x] 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`
- [x] 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`
- [x] Phase 5: MVP - Single test following existing pattern
- Pick minimal test requiring minimal GitClient wrapper
- Follow pattern from `git_clone.rs` and 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:
1. **Validate GRASP conformance** - Ensure all GRASP implementations fully comply with git HTTP protocol
2. **Catch real-world bugs** - Test with actual git client behavior (gzip, protocol versions, edge cases)
3. **Reusable across implementations** - Any GRASP implementation can use these tests for validation
4. **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_domain` parameter
- 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:
1. Requires minimal GitClient wrapper
2. Follows existing `git_clone.rs` pattern
3. Demonstrates end-to-end architecture
4. 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 `OwnerStateDataPushed` fixture
- No new GitClient wrapper needed for MVP - uses reqwest directly
- **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 `flate2` for gzip compression
- Follows existing grasp-audit patterns (relay_domain parameter, reqwest client)
- **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
- **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
- **Architecture Validated:**
- grasp-audit provides test functions accepting `relay_domain` parameter
- ngit-grasp integration tests use TestRelay and call grasp-audit functions
- Pattern is reusable and extensible
- **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_domain` parameter) can support comprehensive git HTTP tests
- ✅ Pattern already exists: `git_clone.rs` makes 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.sh` already manages relay lifecycle
- Tests receive `relay_domain` parameter for HTTP access
- TestRelay is a **convenience**, not a **requirement**
- What's missing: pkt-line parsing, protocol v2 support, comprehensive edge cases
- Proposed architecture:
- Add `grasp-audit/src/git/` module for protocol utilities (pkt_line.rs, protocol.rs, client.rs)
- Expand existing `specs/grasp01/git_clone.rs` with 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
- 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 `bb46` instead of `bb46-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)
- 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:**
```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:**
```bash
# 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 default
- `Content-Encoding: x-gzip` - Alternative gzip identifier
- `Content-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/main` in capability line
**Version 1:**
- `Git-Protocol: version=1` header
- Response starts with `version 1` pkt-line
- Same capability format as v0
**Version 2:**
- `Git-Protocol: version=2` header
- Response starts with `version 2` pkt-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:**
```bash
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-pack` pkt-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:**
1. Gzip-encoded POST bodies (clone, fetch, push)
2. x-gzip encoding variant
3. Truncated gzip streams
4. Empty gzip bodies
5. Mixed encoding (gzip request, identity response)
**Protocol Negotiation:**
1. Client requests v2, server supports v2
2. Client requests v2, server only supports v0 (fallback)
3. Client requests v1
4. No version specified (default to v0)
**Repository States:**
1. Empty repository (no refs)
2. Repository with many refs (>2000 tags test in git)
3. Shallow repository
4. Repository with large objects
5. Repository with submodules
**Operations:**
1. Clone (full)
2. Clone (shallow with --depth)
3. Clone (partial with --filter)
4. Fetch (incremental)
5. Fetch (shallow-since, shallow-exclude)
6. Push (standard)
7. Push (atomic)
8. Push (chunked encoding for large packs)
9. ls-remote
**HTTP Edge Cases:**
1. Chunked Transfer-Encoding
2. Large Content-Length values
3. CONTENT_LENGTH overflow (ssize_t boundary)
4. Empty CONTENT_LENGTH
5. HTTP/1.0 vs HTTP/1.1
6. HTTP/2 (if supported)
7. Keep-alive connections
8. Redirects (301, 302)
9. Path traversal attempts (security)
**Authentication:**
1. No auth (public repo)
2. Basic auth required
3. Auth only for push
4. Auth only for objects (half-auth)
5. Invalid credentials
6. 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):**
1. Content-Encoding: gzip for POST bodies
2. Content-Encoding: x-gzip variant
3. Truncated/malformed gzip handling
**Priority 2 - Protocol Compliance:**
1. Protocol version negotiation (v0, v1, v2)
2. Correct Content-Type headers
3. Correct pkt-line format
4. Capability advertisement
**Priority 3 - Operations:**
1. Clone (basic)
2. Clone (shallow)
3. Fetch (incremental)
4. Push (basic)
5. ls-remote
**Priority 4 - Edge Cases:**
1. Empty repository
2. Large repository (many refs)
3. Chunked encoding
4. 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
1. **Git's gzip handling:** Uses `HTTP_CONTENT_ENCODING` env var, supports both "gzip" and "x-gzip"
2. **Protocol fallback:** Client may request v2 but server can respond with v0 - client must handle gracefully
3. **Half-auth:** Some servers require auth only for objects, not refs - complex to test
4. **Path normalization:** Must handle `/repo.git` vs `/repo` vs `/repo.git/` consistently
5. **Chunked encoding:** Large pushes use chunked encoding when http.postbuffer is small
6. **Empty repos:** Must handle repos with no refs (capabilities^{} line)
7. **Peeled refs:** Annotated tags must show both tag and peeled object
8. **Symref:** HEAD symref must be communicated in capabilities