Skip to content

feat(extensions): fake implementations with error injection#197

Open
behinddwalls wants to merge 1 commit into
scorerfrom
preetam/mock-ext-fakes
Open

feat(extensions): fake implementations with error injection#197
behinddwalls wants to merge 1 commit into
scorerfrom
preetam/mock-ext-fakes

Conversation

@behinddwalls
Copy link
Copy Markdown
Collaborator

@behinddwalls behinddwalls commented Jun 4, 2026

Summary

Why?

Extensions over external systems (changeprovider, buildrunner, pusher,
mergechecker) had no runnable stub to exercise their success — and
especially failure — paths without standing up the real backend
(GitHub/CI/git). scorer and conflict had deterministic stubs (heuristic,
composite, all, none) but no way to inject errors. These fakes let tests
drive both happy and error paths end-to-end from a land request.

What?

  • buildrunner/fake, changeprovider/fake, pusher/fake, mergechecker/fake:
    best-case by default; inject failures via an sq-fake=<token> marker in
    a change URI (e.g. build-fail, conflict, unmergeable, provider-error).
  • scorer/fake, conflict/fake: decorators that wrap an existing impl (the
    "pick") and overlay error injection — scorer via the URI marker
    (score-error) over entity.BatchChanges, analyzer via a predicate, since
    Analyze sees batches, not change URIs.
  • Each fake exposes only New(...) (decorator constructors for scorer/
    conflict). No factory implementations live in extension/* — those belong
    in the wiring layer.
  • pusher/README.md documents the fake.

These packages are test/example stubs, never production. They are wired
into the example orchestrator in a follow-up PR.

Test Plan

  • make test — fake package tests
  • bazel build //...

Issues

Stack

  1. refactor(scorer): score batches over typed change details #207
  2. @ feat(extensions): fake implementations with error injection #197
  3. feat(example): per-queue extension wiring; retire buildrunner/noop #193
  4. refactor(conflict): take a batch of changes as analyzer input #202

Comment on lines +127 to +129
if j := strings.IndexAny(rest, "&#"); j >= 0 {
rest = rest[:j]
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't seem like your test cases exercise this. Probably good to add just to have an example.

}
}
return ""
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we util this? It's duplicated across 5 different fakes..

@behinddwalls behinddwalls force-pushed the preetam/mock-ext-fakes branch from 7b45cdb to b2171aa Compare June 5, 2026 04:44
behinddwalls added a commit that referenced this pull request Jun 5, 2026
## Summary
### Why?

Two "change"-related stores were in the wrong shape. `ChangeStore` — the
real, used store that records per-URI claims for in-flight requests and
backs `start`'s URI claiming and `validate`'s overlap detection — lived
as its own top-level extension and was injected into controllers as a
separate dependency, bypassing the `storage.Storage` aggregator that
owns
every other entity store. Meanwhile `ChangeProviderStore` (exposed via
`Storage.GetChangeProviderStore()`, with a mysql impl, mock, and schema)
was dead code: no controller ever called it, and its
`entity.ChangeProvider`
was orphaned alongside it.

### What?

- Move `ChangeStore` into `package storage`: the interface, mysql impl,
and `change.sql` schema now live under `extension/storage[/mysql]`, and
  `ChangeStore` is a first-class member of the `Storage` aggregator via
  `GetChangeStore()` — matching every other store.
- `start` and `validate` controllers drop their separate `changeStore`
  constructor param and read `store.GetChangeStore()`; the example
  orchestrator no longer constructs/injects it separately.
- Delete the dead `ChangeProviderStore` (interface, mysql, mock,
schema),
`GetChangeProviderStore()`, and the orphaned
`entity/change_provider.go`.
- Fold the standalone changestore integration suite into the shared
`StorageContractSuite` (driven through `GetChangeStore()`); e2e drops
the
  now-redundant changestore schema apply.

## Test Plan
- ✅ `make build`, `make test` (start/validate controllers pass against
the
  storage-package mock)
- ✅ `make lint`, `make check-gazelle`, `make check-mocks`, `make
check-tidy`
  (no drift; `go.mod` / `MODULE.bazel` unchanged)
- ✅ `make integration-test` (storage mysql suite now exercises the
change
  store via `GetChangeStore()`)
- ✅ `make e2e-test` (full land→validate flow: URI claim + overlap
detection,
  `change` table applied from the storage schema dir)

## Issues


## Stack
1. @ #191
1. #195
1. #196
1. #197
@behinddwalls behinddwalls force-pushed the preetam/mock-ext-fakes branch 2 times, most recently from 00c50e7 to a4c97e3 Compare June 5, 2026 05:41
@behinddwalls behinddwalls requested a review from JamyDev June 5, 2026 05:41
behinddwalls added a commit that referenced this pull request Jun 5, 2026
## Summary
### Why?

The scorer took entity.Change (just URIs), so it could not score on real
change size — the example heuristic counted URIs as a placeholder. With
typed change details now persisted on change records, the scorer can
score a
batch on its actual lines/files changed.

### What?

- Add entity.BatchChanges — the normalized, batch-level view of all
changes
  in a batch (BatchID, Queue, []ChangeInfo) with aggregation helpers.
- Scorer.Score now takes entity.BatchChanges; the heuristic ValueFunc
and the
  composite scorer operate over it.
- The score controller resolves each request's change records, flattens
their
  details into BatchChanges, and scores the batch once — replacing the
  per-request multiplicative product over len(URIs).
- Example wiring buckets by total lines changed.

Consumes the typed details persisted by the change-details change.

## Test Plan
- ✅ `make build`, `make test`, `make lint`, `make
check-mocks/gazelle/tidy`
- ✅ `make integration-test`, `make e2e-test` (start -> validate enrich
->
  score normalizes the batch and scores on real change size)

## Issues


## Stack
1. #195
1. @ #196
1. #197
1. #193
1. #202
Base automatically changed from scorer to main June 5, 2026 05:51
## Summary

### Why?

Extensions over external systems (changeprovider, buildrunner, pusher,
mergechecker) had no runnable stub to exercise their success — and
especially failure — paths without standing up the real backend
(GitHub/CI/git). scorer and conflict had deterministic stubs (heuristic,
composite, all, none) but no way to inject errors. These fakes let tests
drive both happy and error paths end-to-end from a land request.

### What?

- buildrunner/fake, changeprovider/fake, pusher/fake, mergechecker/fake:
  best-case by default; inject failures via an `sq-fake=<token>` marker in
  a change URI (e.g. build-fail, conflict, unmergeable, provider-error).
- scorer/fake, conflict/fake: decorators that wrap an existing impl (the
  "pick") and overlay error injection — scorer via the URI marker
  (score-error) over entity.BatchChanges, analyzer via a predicate, since
  Analyze sees batches, not change URIs.
- Each fake exposes only New(...) (decorator constructors for scorer/
  conflict). No factory implementations live in extension/* — those belong
  in the wiring layer.
- pusher/README.md documents the fake.

These packages are test/example stubs, never production. They are wired
into the example orchestrator in a follow-up PR.

## Test Plan

- ✅ `make test` — fake package tests
- ✅ `bazel build //...`
@behinddwalls behinddwalls force-pushed the preetam/mock-ext-fakes branch from a4c97e3 to 1cb6b1f Compare June 5, 2026 05:56
@behinddwalls behinddwalls changed the base branch from main to scorer June 5, 2026 05:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants