Skip to content
Closed
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ build/
coverage/
scripts/
jest.config.ts
jest.setup.js
**/generated/
139 changes: 139 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
# AI Coding Agent Instructions for mock-github

## Project Overview
**mock-github** is a testing library that provides tools to mock GitHub environments locally. It enables testing GitHub Actions and API calls without polluting real repositories or hitting rate limits.

Two core abstractions:
1. **Moctokit**: Mocks GitHub REST API endpoints using OpenAPI spec (via `nock`)
2. **MockGithub**: Mocks full GitHub environment with repositories, branches, files, actions, and env vars

## Architecture & Key Components

### Layer 1: Endpoint Mocking (Foundation)
- `src/endpoint-mocker/abstract-endpoint-mocker.ts`: Base class using `nock` for HTTP interception
- `src/endpoint-mocker/request/abstract-request-mocker.ts`: Generic request parameter matching
- `src/endpoint-mocker/response/abstract-response-mocker.ts`: Response building with status/headers

### Layer 2: API Mocking (Generated)
- `src/moctokit/moctokit.ts`: Extends `EndpointMocker`, wraps auto-generated endpoint methods
- `src/moctokit/generated/endpoint-request.ts`: **AUTO-GENERATED** from GitHub OpenAPI spec (~1817 lines)
- Maps 200+ GitHub API endpoints to `MoctokitRequestMocker` instances
- **Never manually edit** - regenerate via `npm run update:endpoint-requests`
- `src/moctokit/generated/endpoint-details.ts`: OpenAPI metadata for each endpoint

### Layer 3: GitHub Environment Mocking
- `src/github/github-mocker.ts`: Orchestrates setup/teardown of repositories, env vars, actions
- `src/github/repository/repository-mocker.ts`: Creates local git repos via `simple-git`
- `src/github/env/env-mocker.ts`: Manages process environment variables
- `src/github/action/action-mocker.ts`: Simulates GitHub Action execution context

### Type System (Critical for Development)
- Uses **OpenAPI path types** from generated endpoint details
- Generic constraints like `Path extends keyof paths, Method extends keyof paths[Path]`
- `src/moctokit/response/response-mocker.types.ts`: Complex conditional types for response validation
- `.types.ts` files excluded from coverage - contain domain models, not business logic

## Critical Developer Workflows

### Build & Dependencies
```bash
npm run build # TypeScript → JavaScript, alias resolution via tsc-alias
npm test # Jest with 90% coverage threshold
npm run test:report # Coverage report + SonarQube XML output
npm run lint # ESLint with auto-fix on commit via husky
```

### Updating Generated Code
```bash
npm run update:api-spec # Fetch latest GitHub OpenAPI spec
npm run update:endpoint-details # Parse endpoints from spec
npm run update:endpoint-requests # Generate endpoint method bindings
npm run update-all # Run all three steps
```

Scripts are in `scripts/` directory - regenerate when GitHub API changes.

## Project-Specific Patterns

### 1. **Module Path Aliases**
- Use `@mg/*` imports (configured in `tsconfig.json` paths)
- Example: `import { Moctokit } from "@mg/moctokit/moctokit"`
- Resolves to `src/*` during development, `build/src/*` in compiled output

### 2. **Abstract Base Classes for Extension**
- `EndpointMocker`, `RequestMocker`, `ResponseMocker` are abstract - meant to be extended
- `MoctokitRequestMocker` and `MoctokitResponseMocker` extend these for GitHub API specifics
- Pattern: Abstract → Concrete implementation → Usage via interface

### 3. **Config Validation with AJV**
- `MockGithub` constructor validates config against `GithubConfigSchema` (JSON Schema)
- Schema defined in `src/github/schema/` directory
- Config can be JSON file path (string) or object - validated before use

### 4. **Setup/Teardown Lifecycle**
- `MockGithub` requires explicit `setup()` and `teardown()` calls
- Setup order matters: repositories → env vars → actions
- Throws if methods called before setup (e.g., `repo` getter checks `hasCalledSetup`)

### 5. **Repository State Management**
- `RepositoryState` tracks branches, files, remotes (origin/upstream)
- State exposed via `repo.getState()`, `repo.getBranchState()`, etc.
- Branches can be local or pushed - tracked separately in `BranchState`

### 6. **File Organization by Responsibility**
```
src/
├── endpoint-mocker/ # Generic HTTP mocking layer
├── moctokit/ # GitHub API-specific mocking
├── github/ # Full GitHub environment
│ ├── repository/ # Git repo + state management
│ ├── action/ # GitHub Actions simulation
│ ├── env/ # Environment variables
│ └── schema/ # JSON validation schemas
```

## Integration Points & Dependencies

### External Libraries
- `nock`: HTTP mocking (≥1.0.0 incompatible with Node 18 native fetch)
- `simple-git`: Git operations (local repo creation/manipulation)
- `@octokit/rest`: Type definitions (v19, not runtime)
- `ajv`: JSON Schema validation
- `ts-jest`: TypeScript testing

### Cross-Component Communication
1. **Config Flow**: JSON config → validated → distributed to Action/Env/Repo mockers
2. **Setup Coordination**: `MockGithub.setup()` → parallel repo/env setup → then action setup
3. **Query Interface**: `repo.getState()` returns `State` (path, branches, files)

## Testing Patterns

### Mocking Tests
- Located in `test/moctokit/moctokit.test.ts` and `test/github/`
- Create instance → configure mock → execute client → assert response
- Use `repeat` option in `reply()` for multiple matching calls
- Example: `moctokit.rest.repos.get({owner: "kie"}).reply({status: 200, data: {...}})`

### Assertions
- Verify both status and data payload
- Repository tests check git history, branches, file contents via state queries
- Coverage threshold enforced: 90% branches/functions/lines/statements

## Common Pitfalls & Guidelines

- **Don't edit generated files**: `src/moctokit/generated/*` is auto-generated - run `npm run update:*` scripts instead
- **Always setup/teardown MockGithub**: Forgetting causes hard-to-debug state pollution
- **Path aliases in imports**: Use `@mg/` consistently, don't mix relative paths
- **Type-first development**: Leverage generic constraints in request/response mockers - they catch issues early
- **Config schema**: Validate before instantiating - bad config fails at constructor, not later

## Key Files to Reference

| File | Purpose |
|------|---------|
| `src/index.ts` | Public API exports |
| `src/github/github-mocker.ts` | Main entry point for environment mocking |
| `src/moctokit/moctokit.ts` | Main entry point for API mocking |
| `jest.config.ts` | Coverage thresholds (90%), path aliases, `ts-jest` config |
| `package.json` | Scripts, build output points, dependencies |
| `scripts/endpoint-details.js` | Generates endpoint metadata from OpenAPI |
2 changes: 1 addition & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: 16
node-version: 24
registry-url: https://registry.npmjs.org/

- name: Install packages
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/pull_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
tests:
strategy:
matrix:
node-version: [16.x, 20.x]
node-version: 24.x
Comment thread
mareknovotny marked this conversation as resolved.
Outdated
os: [ubuntu-latest]
fail-fast: false
runs-on: ${{ matrix.os }}
Expand All @@ -36,7 +36,7 @@ jobs:

- uses: actions/setup-node@v6
with:
node-version: 20.x
node-version: 24.x

- name: Install packages
run: npm ci
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
uses: warchant/setup-sonar-scanner@v10
- uses: actions/setup-node@v6
with:
node-version: 20
node-version: 24
- run: npm ci
- run: npm run test:report
env:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/update.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: 20
node-version: 24
cache: npm
- run: npm install @octokit/openapi-types-ghec -D
- run: npm ci
Expand Down
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
24
1 change: 1 addition & 0 deletions jest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const jestConfig: Config.InitialOptions = {
},
clearMocks: true,
resetMocks: true,
setupFilesAfterEnv: ["<rootDir>/jest.setup.js"],
collectCoverageFrom: [
"src/**",
"!**/*.types.ts",
Expand Down
10 changes: 10 additions & 0 deletions jest.setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Suppress deprecation warning from @octokit/request-error
// The library warns about using error.code instead of error.status
// This can be safely suppressed as the functionality is not affected
const originalWarn = console.warn;
console.warn = function (...args) {
if (args[0] && typeof args[0] === 'string' && args[0].includes('[@octokit/request-error]')) {
return;
}
originalWarn.apply(console, args);
};
Loading