{"id":36900642,"url":"https://github.com/zntrio/extproctor","last_synced_at":"2026-01-12T15:46:45.423Z","repository":{"id":329083849,"uuid":"1114346064","full_name":"zntrio/extproctor","owner":"zntrio","description":"ExtProctor is a test runner for Envoy External Processing (ExtProc) implementations. Define test cases using human-readable prototext manifests, validate expected behaviours across all processing phases (headers, body, trailers), and integrate seamlessly into CI/CD pipelines with JSON output and golden file support.","archived":false,"fork":false,"pushed_at":"2025-12-17T07:19:14.000Z","size":71,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-12-20T20:52:26.518Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/zntrio.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-12-11T08:41:27.000Z","updated_at":"2025-12-17T07:17:45.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/zntrio/extproctor","commit_stats":null,"previous_names":["zntrio/extproctor"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/zntrio/extproctor","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zntrio%2Fextproctor","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zntrio%2Fextproctor/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zntrio%2Fextproctor/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zntrio%2Fextproctor/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zntrio","download_url":"https://codeload.github.com/zntrio/extproctor/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zntrio%2Fextproctor/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28341820,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-12T12:22:26.515Z","status":"ssl_error","status_checked_at":"2026-01-12T12:22:10.856Z","response_time":98,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-01-12T15:46:45.036Z","updated_at":"2026-01-12T15:46:45.397Z","avatar_url":"https://github.com/zntrio.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n# 🧪 ExtProctor\n\n**A test runner for Envoy ExtProc implementations**\n\n[![Go Version](https://img.shields.io/badge/Go-1.24+-00ADD8?style=flat\u0026logo=go)](https://go.dev/)\n[![License](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)\n[![Go Report Card](https://goreportcard.com/badge/zntr.io/extproctor)](https://goreportcard.com/report/zntr.io/extproctor)\n[![Documentation](https://img.shields.io/badge/docs-reference-blue)](https://pkg.go.dev/zntr.io/extproctor)\n\n[Features](#features) •\n[Installation](#installation) •\n[Quick Start](#quick-start) •\n[Documentation](#documentation) •\n[Examples](#examples) •\n[Contributing](#contributing)\n\n\u003c/div\u003e\n\n---\n\n## Why ExtProctor?\n\nImplementing and evolving [Envoy External Processing (ExtProc)](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/ext_proc_filter) services is error-prone. Behaviours depend on correct protobuf message structures and a sequence of callbacks that are difficult to test manually.\n\n\u003e [!NOTE]\n\u003e This project is heavily inspired by [Google Service Extensions](https://github.com/GoogleCloudPlatform/service-extensions) where they use the same approach to test their service extensions.\n\n**ExtProctor** provides a dedicated test runner that enables:\n\n- ✅ **Automated regression testing** for ExtProc implementations\n- ✅ **Fast feedback loops** during local development\n- ✅ **CI/CD integration** with machine-readable outputs\n- ✅ **Version-controlled test cases** using human-readable prototext manifests\n\n## Features\n\n| Feature | Description |\n|---------|-------------|\n| 📝 **Prototext Manifests** | Define test cases using human-readable prototext format |\n| 🔄 **Full ExtProc Support** | Test all processing phases: headers, body, and trailers |\n| 📸 **Golden Files** | Capture and compare responses using the golden file pattern |\n| ⚡ **Parallel Execution** | Run tests concurrently for faster feedback |\n| 🏷️ **Flexible Filtering** | Filter tests by name pattern or tags |\n| 📊 **Multiple Output Formats** | Human-readable or JSON output for CI integration |\n| 🔌 **Unix Socket Support** | Connect to ExtProc services via Unix domain sockets |\n| 🔒 **TLS Support** | Secure gRPC connections with client certificates |\n\n## Installation\n\n### Using Go\n\n```bash\ngo install zntr.io/extproctor/cmd/extproctor@latest\n```\n\n### From Source\n\n```bash\ngit clone https://github.com/zntrio/extproctor.git\ncd extproctor\ngo build -o extproctor ./cmd/extproctor\n```\n\n### Verify Installation\n\n```bash\nextproctor --help\n```\n\n## Quick Start\n\n### 1. Create a Test Manifest\n\nCreate a file `tests/basic.textproto`:\n\n```prototext\nname: \"basic-test\"\ndescription: \"Basic ExtProc test\"\n\ntest_cases: {\n  name: \"add-header\"\n  description: \"Verify ExtProc adds a custom header\"\n  tags: [\"smoke\"]\n\n  request: {\n    method: \"GET\"\n    path: \"/api/v1/users\"\n    scheme: \"https\"\n    authority: \"api.example.com\"\n    headers: {\n      key: \"content-type\"\n      value: \"application/json\"\n    }\n  }\n\n  expectations: {\n    phase: REQUEST_HEADERS\n    headers_response: {\n      set_headers: {\n        key: \"x-custom-header\"\n        value: \"custom-value\"\n      }\n    }\n  }\n}\n```\n\n### 2. Run the Tests\n\n```bash\nextproctor run ./tests/ --target localhost:50051\n```\n\n### 3. View Results\n\n```\nRunning tests from 1 manifest(s)...\n\n✓ basic-test/add-header (12ms)\n\nResults: 1 passed, 0 failed, 0 skipped\n```\n\n## Documentation\n\n### CLI Commands\n\n#### `extproctor run`\n\nExecute tests against an ExtProc service.\n\n```bash\n# Run all tests in a directory\nextproctor run ./tests/ --target localhost:50051\n\n# Run with Unix domain socket\nextproctor run ./tests/ --unix-socket /var/run/extproc.sock\n\n# Run with parallel execution\nextproctor run ./tests/ --target localhost:50051 --parallel 4\n\n# Filter by test name pattern\nextproctor run ./tests/ --target localhost:50051 --filter \"auth*\"\n\n# Filter by tags\nextproctor run ./tests/ --target localhost:50051 --tags \"smoke,regression\"\n\n# JSON output for CI pipelines\nextproctor run ./tests/ --target localhost:50051 --output json\n\n# Verbose mode for debugging\nextproctor run ./tests/ --target localhost:50051 -v\n\n# Update golden files\nextproctor run ./tests/ --target localhost:50051 --update-golden\n```\n\n#### `extproctor validate`\n\nValidate manifest syntax without running tests.\n\n```bash\n# Validate all manifests in a directory\nextproctor validate ./tests/\n\n# Validate specific files\nextproctor validate test1.textproto test2.textproto\n```\n\n#### `extproctor fmt`\n\nFormat textproto manifest files using [txtpbfmt](https://github.com/protocolbuffers/txtpbfmt).\n\n```bash\n# Format a single file to stdout\nextproctor fmt test.textproto\n\n# Format files in-place\nextproctor fmt --write ./tests/\n\n# Show diff of what would change\nextproctor fmt --diff ./tests/\n\n# Format specific files in-place\nextproctor fmt -w test1.textproto test2.textproto\n\n# CI check - returns error if files need formatting\nextproctor fmt ./tests/\n```\n\n### Command-Line Options\n\n#### Run Command Options\n\n| Flag | Description | Default |\n|------|-------------|---------|\n| `--target` | ExtProc service address (host:port) | `localhost:50051` |\n| `--unix-socket` | Unix domain socket path | — |\n| `--tls` | Enable TLS for gRPC connection | `false` |\n| `--tls-cert` | TLS client certificate file | — |\n| `--tls-key` | TLS client key file | — |\n| `--tls-ca` | TLS CA certificate file | — |\n| `-p, --parallel` | Number of parallel test executions | `1` |\n| `-o, --output` | Output format (`human`, `json`) | `human` |\n| `-v, --verbose` | Enable verbose output | `false` |\n| `--filter` | Filter tests by name pattern | — |\n| `--tags` | Filter tests by tags (comma-separated) | — |\n| `--update-golden` | Update golden files with actual responses | `false` |\n\n\u003e **Note:** `--target` and `--unix-socket` are mutually exclusive.\n\n#### Fmt Command Options\n\n| Flag | Description | Default |\n|------|-------------|---------|\n| `-w, --write` | Write formatted output back to files (in-place) | `false` |\n| `-d, --diff` | Show diff of what would change | `false` |\n\n### Manifest Format\n\nTest manifests are written in [Prototext](https://protobuf.dev/reference/protobuf/textformat-spec/) format.\n\n#### Structure\n\n```prototext\nname: \"manifest-name\"\ndescription: \"Description of the test suite\"\n\ntest_cases: {\n  name: \"test-case-name\"\n  description: \"What this test validates\"\n  tags: [\"tag1\", \"tag2\"]\n\n  request: {\n    method: \"POST\"\n    path: \"/api/endpoint\"\n    scheme: \"https\"\n    authority: \"api.example.com\"\n    headers: {\n      key: \"content-type\"\n      value: \"application/json\"\n    }\n    body: '{\"key\": \"value\"}'\n    process_request_body: true\n    process_response_headers: true\n  }\n\n  expectations: {\n    phase: REQUEST_HEADERS\n    headers_response: {\n      set_headers: {\n        key: \"x-custom\"\n        value: \"value\"\n      }\n    }\n  }\n}\n```\n\n#### Processing Phases\n\n| Phase | Description |\n|-------|-------------|\n| `REQUEST_HEADERS` | Processing request headers |\n| `REQUEST_BODY` | Processing request body |\n| `REQUEST_TRAILERS` | Processing request trailers |\n| `RESPONSE_HEADERS` | Processing response headers |\n| `RESPONSE_BODY` | Processing response body |\n| `RESPONSE_TRAILERS` | Processing response trailers |\n\n#### Expectation Types\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eHeaders Response\u003c/strong\u003e\u003c/summary\u003e\n\n```prototext\nexpectations: {\n  phase: REQUEST_HEADERS\n  headers_response: {\n    set_headers: {\n      key: \"x-custom\"\n      value: \"value\"\n    }\n    remove_headers: \"x-internal\"\n    append_headers: {\n      key: \"x-multi\"\n      value: \"value\"\n    }\n  }\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eBody Response\u003c/strong\u003e\u003c/summary\u003e\n\n```prototext\nexpectations: {\n  phase: REQUEST_BODY\n  body_response: {\n    body: '{\"modified\": true}'\n    common_response: {\n      status: CONTINUE_AND_REPLACE\n    }\n  }\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eTrailers Response\u003c/strong\u003e\u003c/summary\u003e\n\n```prototext\nexpectations: {\n  phase: REQUEST_TRAILERS\n  trailers_response: {\n    set_trailers: {\n      key: \"x-checksum-validated\"\n      value: \"true\"\n    }\n  }\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eImmediate Response (Short-circuit)\u003c/strong\u003e\u003c/summary\u003e\n\n```prototext\nexpectations: {\n  phase: REQUEST_HEADERS\n  immediate_response: {\n    status_code: 403\n    headers: {\n      key: \"content-type\"\n      value: \"application/json\"\n    }\n    body: '{\"error\": \"forbidden\"}'\n  }\n}\n```\n\n\u003c/details\u003e\n\n#### Golden Files\n\nUse golden files for snapshot testing:\n\n```prototext\ntest_cases: {\n  name: \"golden-test\"\n  request: { ... }\n  golden_file: \"golden/test-response.textproto\"\n}\n```\n\nUpdate golden files when behavior changes intentionally:\n\n```bash\nextproctor run ./tests/ --target localhost:50051 --update-golden\n```\n\n## Examples\n\nThe [`testdata/examples/`](testdata/examples) directory contains complete example manifests:\n\n| File | Description |\n|------|-------------|\n| [`basic_headers.textproto`](testdata/examples/basic_headers.textproto) | Basic header processing (add/remove headers) |\n| [`auth_flow.textproto`](testdata/examples/auth_flow.textproto) | Authentication flow with immediate response rejection |\n| [`body_processing.textproto`](testdata/examples/body_processing.textproto) | Request body inspection and transformation |\n| [`multi_phase_flow.textproto`](testdata/examples/multi_phase_flow.textproto) | Multi-phase processing across request/response lifecycle |\n\n### Sample ExtProc Server\n\nA sample ExtProc server is included for testing and reference:\n\n```bash\n# Start the sample server\ngo run ./sample/extproc/ --addr :50051\n\n# Run tests against it\nextproctor run ./sample/extproc/test/ --target localhost:50051\n```\n\nThe sample server demonstrates:\n- Request headers processing with custom header injection\n- Request/response body handling\n- Response headers modification\n- gRPC health check endpoint\n\n## Development\n\n### Prerequisites\n\n- [Go 1.24+](https://go.dev/dl/)\n- [Buf CLI](https://buf.build/docs/installation) (for protobuf generation)\n\n### Building\n\n```bash\ngo build -o extproctor ./cmd/extproctor\n```\n\n### Running Tests\n\n```bash\ngo test ./...\n```\n\n### Regenerating Protobuf Code\n\n```bash\nbuf generate\n```\n\n### Project Structure\n\n```\nextproctor/\n├── cmd/extproctor/          # CLI entry point\n├── internal/\n│   ├── cli/              # Command-line interface\n│   ├── client/           # ExtProc gRPC client\n│   ├── comparator/       # Response comparison logic\n│   ├── golden/           # Golden file handling\n│   ├── manifest/         # Manifest loading and validation\n│   ├── reporter/         # Test result reporting\n│   └── runner/           # Test execution engine\n├── proto/                # Protobuf definitions\n├── sample/extproc/       # Sample ExtProc server\n└── testdata/examples/    # Example test manifests\n```\n\n## Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request.\n\n1. Fork the repository\n2. Create your feature branch (`git checkout -b feature/amazing-feature`)\n3. Commit your changes (`git commit -m 'Add some amazing feature'`)\n4. Push to the branch (`git push origin feature/amazing-feature`)\n5. Open a Pull Request\n\nPlease make sure to:\n- Update tests as appropriate\n- Follow the existing code style\n- Update documentation for any new features\n\n## Related Resources\n\n- [Envoy ExtProc Documentation](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/ext_proc_filter)\n- [Envoy ExtProc Proto Definition](https://github.com/envoyproxy/envoy/blob/main/api/envoy/service/ext_proc/v3/external_processor.proto)\n- [Prototext Format Specification](https://protobuf.dev/reference/protobuf/textformat-spec/)\n\n## License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n\n---\n\n\u003cdiv align=\"center\"\u003e\n\nMade with ❤️ for the Envoy community\n\n\u003c/div\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzntrio%2Fextproctor","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzntrio%2Fextproctor","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzntrio%2Fextproctor/lists"}