A Ruby + RSpec test suite for GitHub's GET /repos/:owner/:repo endpoint. Showcases API testing patterns: custom matchers, dependency injection, defensive testing, and reusable test components.
Standard (just needs Ruby >=4.0.2 and Bundler):
bundle install
bundle exec rspecWith pixi (optional — I use this to pin the Ruby version without relying on a system install):
pixi run install
pixi run testpixi run install and pixi run test are just aliases for the two bundle commands above, defined in pixi.toml. If you already have Ruby >=4.0.2 set up locally you don't need pixi at all.
No GitHub auth token is needed — all endpoints tested here are public. If you're on a shared network and hit the 60 req/hr rate limit, you can pass a token:
GITHUB_TOKEN=ghp_xxxxxxxxxxxxxxxx pixi run testYou can also override the target API environment via GITHUB_API_URL:
GITHUB_TOKEN=<your_token> GITHUB_API_URL=<your_custom_url> pixi run testlib/
github_client.rb HTTP client built on Faraday. Handles connection setup,
retries, JSON parsing. Returns a Response struct so that
tests never deal with Faraday directly.
endpoints/
repos.rb Wraps GET /repos/:owner/:repo into a method call.
If I needed to test more endpoints later I'd just add
another file here (e.g. users.rb, issues.rb).
spec/
spec_helper.rb Entry point — requires all dependencies and creates
a shared GitHub::Client that every test uses.
repos_spec.rb The actual test cases. Organized into contexts:
happy path, field-level validation, negative cases,
and one extra repo to prove the shared examples work.
support/
response_matchers.rb Two custom matchers (have_status, have_json_field).
The main reason I wrote these is so failures can print
something useful instead of just "expected true, got false".
shared_examples.rb A set of checks that any successful repo response should
pass (status 200, has id, has full_name, etc.).
Used by both the "octocat" and "torvalds (Father of Linux)"
contexts.
5 contexts, 24 examples total:
- Happy path on
octocat/Hello-World— status 200, field checks (full_name, description, default_branch), plus the shared examples - Field-level validation — types and formats: visibility string, fork boolean, ISO-8601 date parsing, stargazers as integer, nullable license object
- Non-existent repo — expects 404 with a structured error body (message + documentation_url)
- Empty owner — passes
//Hello-Worldas a malformed path and asserts 4xx. I usedbe_between(400, 499)instead of hardcoding 404 since the exact code depends on how GitHub routes the request - Second repo (
torvalds/linux) — runs the same shared examples on different data to prove the assertions generalize
The suite relies on octocat/Hello-World and torvalds/linux staying up — both are stable public repos, but if either disappears the fix is just swapping the owner/repo strings.
Without an auth token the GitHub API allows 60 requests/hour per IP. That's fine for local runs, but on a shared network or in CI you'll want to pass a token via GITHUB_TOKEN.