Files
Ben Sabic cd7b6af1c8 test(integration-tests): add Emulate.dev-backed tests for the GitHub adapter (#479)
## Summary

Add an Emulate.dev-backed integration-test suite for the GitHub adapter,
mirroring the structure of the Slack work in #477. Tests drive the
adapter against an in-process
[`@emulators/github`](https://emulate.dev/docs/github) server and assert
on its stateful store (comments, reviews) rather than `mock.calls`,
catching wire-format and contract issues that pure Octokit mocks miss.

- New private devDeps in `packages/integration-tests`:
`@emulators/github`, `@emulators/core`, `@hono/node-server`, plus a
workspace dep on `@chat-adapter/github`. (`@emulators/core` and
`@hono/node-server` are also declared by #477; this PR is independent of
merge order.)
- New harness `packages/integration-tests/src/github-emulator-utils.ts`
boots the emulator on an ephemeral `127.0.0.1` port, seeds a
deterministic user / repo / issue / PR / starter review comment, and
exposes a near-passthrough HTTP forwarder. **No re-signing needed**
here. `@emulators/core`'s `WebhookDispatcher` already signs deliveries
with `X-Hub-Signature-256: sha256=<hex>` exactly as the GitHub adapter
expects. The harness also adds a small URL rewriter for Octokit's
`pulls.createReplyForReviewComment` shortcut endpoint, translating it
into the canonical review-comment POST that the emulator implements.
- Four new test files (12 tests), wired to the adapter via its existing
`apiUrl` + `webhookSecret` config **zero source changes** to
`packages/adapter-github`:
- `emulator-github-auth.test.ts` (2) `GET /user` populates `botUserId`
during `initialize()`.
- `emulator-github-comments.test.ts` (4) `thread.post` / `edit` /
`delete` on issue and PR-conversation threads.
- `emulator-github-reviews.test.ts` (3) review-comment replies routed
through `pulls.createReplyForReviewComment` with the right
\`in_reply_to_id\`, plus edit / delete.
- `emulator-github-events.test.ts` (3) full inbound `issue_comment` /
`pull_request_review_comment` round-trip, including bot self-message
filtering.

```mermaid
flowchart LR
  subgraph Test["Vitest test process"]
    SDK[GitHubAdapter + Chat]
    Forwarder["HTTP forwarder<br/>passthrough"]
    Emu["@emulators/github<br/>(in-process Hono)"]
  end

  SDK -->|"issues.createComment / pulls.* / GET /user<br/>(apiUrl override)"| Emu
  Emu -->|"X-Hub-Signature-256 + x-github-event"| Forwarder
  Forwarder -->|"chat.webhooks.github(request)"| SDK
```

### Out of scope (deliberate)

- **Reactions** `@emulators/github` does not implement the `/reactions`
endpoints used by the adapter. Reaction logic is still covered by the
existing mock-based tests in
`packages/adapter-github/src/index.test.ts`.
- **GitHub App auth** (JWT \u2192 installation token via `POST
/app/installations/:id/access_tokens`) the adapter and emulator both
support it, but PAT-mode was the agreed scope here.
- **Multi-tenant install flows** via `installation` webhook events.
- Branches/refs, releases, search, actions, checks not used by the
adapter.

## Test plan

- [x] `pnpm --filter @chat-adapter/integration-tests test` 407 tests
pass across 34 files (including the 12 new emulator-github tests, ~480
ms total).
- [x] `pnpm check` and `pnpm knip` clean.
- [x] CI safety verified: ephemeral ports (`port: 0`), loopback-only
binds (`127.0.0.1`), deterministic teardown via `httpServer.close()`, no
env vars, no external network egress.

## Checklist

- [x] All commits are signed and verified
- [x] \`pnpm validate\` passes
- [x] Changeset added (or N/A see [CONTRIBUTING.md](./CONTRIBUTING.md))
N/A: \`@chat-adapter/integration-tests\` is \`private: true\` and the
change is test-only.
- [x] Documentation updated (or N/A)
\`packages/integration-tests/README.md\` describes the new
emulator-github test category.

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-05-13 07:12:55 +10:00

35 lines
1007 B
JSON

{
"name": "@chat-adapter/integration-tests",
"version": "4.0.0",
"private": true,
"description": "Integration tests for chat",
"type": "module",
"scripts": {
"test": "vitest run --coverage",
"test:watch": "vitest",
"typecheck": "tsc --noEmit"
},
"devDependencies": {
"@emulators/core": "^0.5.0",
"@emulators/github": "^0.5.0",
"@emulators/slack": "^0.5.0",
"@hono/node-server": "^2.0.2",
"@types/node": "^25.3.2",
"typescript": "^5.7.2",
"vitest": "^4.0.18"
},
"dependencies": {
"@chat-adapter/discord": "workspace:*",
"@chat-adapter/gchat": "workspace:*",
"@chat-adapter/github": "workspace:*",
"@chat-adapter/messenger": "workspace:*",
"@chat-adapter/slack": "workspace:*",
"@chat-adapter/state-memory": "workspace:*",
"@chat-adapter/teams": "workspace:*",
"@chat-adapter/telegram": "workspace:*",
"@chat-adapter/whatsapp": "workspace:*",
"chat": "workspace:*",
"discord-api-types": "^0.37.119"
}
}