{"id":46126842,"url":"https://github.com/raphi011/handoff","last_synced_at":"2026-03-02T02:50:26.168Z","repository":{"id":68429374,"uuid":"597140573","full_name":"raphi011/handoff","owner":"raphi011","description":"Testrunner service for Go","archived":false,"fork":false,"pushed_at":"2026-02-01T12:46:18.000Z","size":10020,"stargazers_count":6,"open_issues_count":2,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2026-02-01T22:14:46.288Z","etag":null,"topics":["go","golang","testing"],"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/raphi011.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","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":"2023-02-03T18:07:05.000Z","updated_at":"2025-07-08T19:40:09.000Z","dependencies_parsed_at":"2023-09-26T22:40:37.506Z","dependency_job_id":"cb4d5b0c-e2f3-4b2b-9add-f6a1035676a5","html_url":"https://github.com/raphi011/handoff","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/raphi011/handoff","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/raphi011%2Fhandoff","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/raphi011%2Fhandoff/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/raphi011%2Fhandoff/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/raphi011%2Fhandoff/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/raphi011","download_url":"https://codeload.github.com/raphi011/handoff/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/raphi011%2Fhandoff/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29991298,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-02T01:47:34.672Z","status":"online","status_checked_at":"2026-03-02T02:00:07.342Z","response_time":60,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["go","golang","testing"],"created_at":"2026-03-02T02:50:25.276Z","updated_at":"2026-03-02T02:50:26.162Z","avatar_url":"https://github.com/raphi011.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 🤝 Handoff\n\n![Go Tests](https://github.com/raphi011/handoff/actions/workflows/go-tests.yml/badge.svg)\n\nHandoff is a library that allows you to bootstrap a server that runs scheduled and manually triggered e2e tests written in Go and is extensible through plugins.\n\n## Why Handoff?\n\nMore and more companies are building their software as distributed systems.\n\nThese are notoriously hard to test end-to-end as the number of services grow. At some point starting all of them and their dependencies locally on the developer's machine becomes unfeasible.\n\nThis means that if you want to make sure your system works as expected you need to run tests in an environment that is as close to production as possible, such as a development or staging cluster.\n\nThis is where Handoff comes in. You can run Handoff alongside your system as a standalone server that runs all your end-to-end tests either\n\n* on demand (via cli, api or ui) or\n* repeatedly through a configurable schedule\n\nOn top of that there is a baked in web ui where you can look up the test results and manually trigger new runs.\n\nOther valuable (future/planned) features:\n\n* Matching and linking test runs to logs / traces / metrics generated by the\nsystems under test (SUT) for easier debugging.\n* Running smoke tests on new deployments for automatic rollbacks if necessary (argocd, flux).\n* Fighting flaky tests via\n * auto detection\n * triggering multiple scheduled runs of the same test suite to provoke a test failure which can be debugged thereafter\n* Maintaining historic test run data for deeper insights on the\nstability of the platform and making it available via a `/metrics` endpoint.\n* Triggering of alerts on test failures (e.g. via pagerduty).\n* Automated notifications on test runs (e.g. via slack messages).\n* Github Integration: adding test run results to relevant PRs after they were merged and deployed.\n\n## Example\n\nBootstrapping a server is simple, all you need to do is run this code:\n\n```go\npackage main\n\nfunc main() {\n\th := handoff.New()\n\th.Run()\n}\n```\n\nTo pass in test suites and scheduled runs you can do that by passing in `handoff.WithTestSuite` and `handoff.WithScheduledRun` options to `handoff.New()`.\n\nAnother way is to register them via `handoff.Register` before calling `handoff.New()`. This is especially convenient when you want to have your tests in the same repository as the system under test (SUT), which means they would be in a different repository (unless you have a monorepo). In this case the test package could register the tests in an init function like so:\n\n```go\nfunc init() {\n\thandoff.Register(ts, scheduledRuns)\n}\n```\n\nand then all the handoff server needs to do is import the test package with a blank identifier:\n\n```go\nimport _ \"github.com/my-org/my-service/tests\"\n```\n\nFor examples see [./cmd/example-server-bootstrap/main.go] and [./internal/packagetestexample].\n\n## Build\n\n```sh\ntempl generate\ngo build ./cmd/example-server-bootstrap/\n./example-server-bootstrap\n```\n\n## Run\n\n```sh\n./example-server-bootstrap\n```\n\n## Live reload\n\nInstead of generating templ files and building the binary you can also run the example server with live reload:\n\n```sh\nair\n```\n\n## Web\n\nIf your server is running you can open your browser to e.g. `http://localhost:1337/suites`.\n\nThis will show you all available test suites.\n\nTo create a new test run you can use the post request in `./requests.http`\n\n```sh\nhttpyac requests.http\n```\n\n## Local dev cluster\n\nPrerequisites:\n\n* Running Docker\n* Tilt + Kind installed and on the path\n\nRun\n\n```sh\nkind create cluster --config=kind-config.yaml\ntilt up\n```\n\nIf you press `\u003cspace\u003e` the tilt UI will open up.\n\nOnce handoff is green in the dashboard you should be able to open up the ui here: \u003chttp://localhost:1337/\u003e.\n\n## Test best practices\n\n* Pass in the test context for longer running operations and check if it was cancelled.\n* Only log messages via t.Log/t.Logf as other log messages will not show up in the test logs.\n* Make sure that code in `setup` is idempotent as it can run more than once.\n\n## Planned features\n\nSee [here](./docs/FEATURES.md).\n\n## Non goals\n\n* Implement a new assertion library. We aim to be compatible with existing ones.\n\n## Metrics\n\nMetrics are exposed via the `/metrics` endpoint.\n\n| Name                             | Type    | Description                                 | Labels                        |\n| -------------------------------- | ------- | ------------------------------------------- | ----------------------------- |\n| handoff_testsuites_running       | gauge   | The number of test suites currently running | namespace, suite_name         |\n| handoff_testsuites_started_total | counter | The number of test suite runs started       | namespace, suite_name, result |\n| handoff_tests_run_total          | counter | The number of tests run                     | namespace, suite_name, result |\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fraphi011%2Fhandoff","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fraphi011%2Fhandoff","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fraphi011%2Fhandoff/lists"}