Audit: record measured results — 33.0% to 98.1%, 5 defects found

d5679e83572c · AtlantisPleb · · parent f71da8a5f501

Audit: record measured results — 33.0% to 98.1%, 5 defects found

The issues/projects layer went from 33.0% mean coverage with 23 modules at
zero to 98.1% with none. Repo total 79.14% -> 83.14%; suite 939 -> 1218.

Records what writing the tests actually found, which was the point:
ProjectShowLive raised KeyError on every mount (module attribute @statuses
read as an assign inside ~H) so the project board had never rendered for
anyone; two 500s in ProjectController; and values silently dropped by a
dead atom-key clause in Projects.create_project_item/2.

Also records the compatibility gaps that were pinned as current behaviour
rather than changed unilaterally -- including ProjectController ignoring
:username across five actions, which makes another user's project readable
and writable -- and the dead ProjectItems/ProjectFields context modules.

Deploy story

What this commit did to the running system — joined from the forge receipt chain, the part a commit page elsewhere cannot show.

Not deployed through the forge lane

No push, promotion, build, or deploy receipt references this commit (receipts are scanned over a bounded recent window). Changes shipped by full node replacement carry their proof in the release gate receipt instead.

Changed files

  • modified docs/2026-08-20-test-coverage-audit.md

Diff

1 file changed, +68 -11

docs/2026-08-20-test-coverage-audit.md modified +68 -11

@@ -132,17 +132,74 @@ the port can be blamed for or expected to have fixed.

132 132
133 133
## 5. Actions
134 134
135
In progress at the time of writing, in response to this audit. This section states intent,
136
not completed work — check the commits following `dcbe8a4` for what actually landed:
137
138
1. Tests written for the six untested `/api/v3` controllers and three JSON views, against
139
   `docs/github-api-issues-projects-assessment.md` as the contract — including error paths
140
   (404, 422), since status codes are part of a compatibility promise.
141
2. Tests written for all eight issues/projects LiveViews: mount, seeded records, empty
142
   state, and at least one interaction each. Assertions target ids, `aria-*`, and visible
143
   text rather than CSS classes, since this repo just migrated off DaisyUI onto basecoat.
144
3. `ProjectFields` and `ProjectItems` contexts and schemas covered; `Projects` and `Issues`
145
   raised from 30.6% and 37.8%. The two never-executed fixtures verified rather than assumed.
135
Completed. Measured after the work landed:
136
137
| | before | after |
138
|---|---|---|
139
| issues/projects layer, mean coverage | 33.0% | **98.1%** |
140
| issues/projects modules at 0% | 23 | **0** |
141
| repo total | 79.14% | **83.14%** |
142
| suite | 939 passing | **1218 passing** (+9 cluster) |
143
144
279 tests added across three parallel efforts:
145
146
1. **`/api/v3` steps 6–9** — 81 tests. All six untested controllers and three JSON views went
147
   from 0% to 89–100%, written against `docs/github-api-issues-projects-assessment.md`,
148
   including 404/422 error paths since status codes are part of a compatibility promise.
149
2. **The eight LiveViews** — 57 tests, all from 0% to 97–100%. Mount, seeded records, empty
150
   state, and every interaction each view has. Assertions target ids, `aria-*`, `role`, and
151
   visible text rather than CSS classes, since this repo just migrated off DaisyUI.
152
3. **Domain contexts** — 141 tests. `ProjectFields`, `ProjectItems`, `Projects`, `Milestones`,
153
   `Labels` to 100%; `Issues` from 37.8% to 96.9%. Both never-executed fixtures verified.
154
155
### What writing the tests found
156
157
Coverage was not the point; this was. Five defects, three of them user-visible:
158
159
- **`ProjectShowLive` was completely broken in production.** `@statuses` is a module
160
  attribute, but line 109 uses it *inside* `~H`, where `@statuses` means `assigns.statuses` —
161
  which `mount/3` never assigned (it assigns `:status_options`). Every request to
162
  `/:owner/:repo/projects/:number` raised `KeyError`. The project board had never rendered
163
  for anyone. This is the clearest argument for the audit: a 0%-coverage page was 100% broken
164
  and nothing said so.
165
- **Two 500s in `ProjectController`:** a missing or non-numeric `issue_number` reached
166
  `Repo.get_by!(Issue, number: nil)` (`ArgumentError`), and a non-map `values` reached
167
  `Map.merge` (`BadMapError`). Both now 422. Notably the *create* path already returned a
168
  correct 422 for the same input — the two paths disagreed.
169
- **Values silently dropped** in `Projects.create_project_item/2`: an atom-key clause read
170
  `attrs["values"]`, always `nil` for an atom-keyed map, so values were discarded on insert.
171
  The clause had no caller and was a strictly worse duplicate of the one below it.
172
173
### Findings reported rather than silently encoded
174
175
These are GitHub-compatibility gaps a real `gh`/Octokit client would hit. They were pinned as
176
current behaviour in tests, not "fixed" unilaterally:
177
178
- **`ProjectController` ignores `:username`** in `show`, `items`, `create_item`,
179
  `update_item`, and `fields` — verified, all five destructure it as `_username`; only
180
  `index` filters by owner. A project owned by `alice` is readable *and writable* at
181
  `/users/bob/projectsV2/:n`. Consistent across all five, so it reads as deliberate
182
  simplification, but it is an authorization gap rather than a shape mismatch.
183
- **`AssigneeController` is a hardcoded stub** — `index` always returns `%{assignees: []}`,
184
  `show` always 404s, so no user is ever reported assignable, while
185
  `POST .../issues/:n/assignees` accepts any login.
186
- `PATCH /repos/:owner/:repo/labels/:name` cannot rename a label (GitHub uses `new_name`;
187
  the path's `name` always wins and `new_name` is silently ignored).
188
- `POST .../issues/:n/labels` 404s for a label that does not exist yet, where GitHub creates
189
  it on the fly — and that 404 is indistinguishable from "issue not found".
190
- `DELETE .../issues/:n/labels/:name` is a silent no-op when the label is not on the issue;
191
  GitHub returns 404.
192
- `LabelJSON` renders `url` with `URI.encode_www_form/1` (spaces → `+`) while lookup decodes
193
  with `URI.decode/1`, so a label named `good first issue` renders a URL its own `show`
194
  endpoint cannot resolve.
195
196
### Dead code identified
197
198
`OpenAgents.ProjectItems` and `OpenAgents.ProjectFields` (the *context* modules, not the
199
schemas) have **zero callers in `lib/`** — generator scaffolding. The live paths go through
200
`Projects.*` instead. They are duplication with divergent semantics:
201
`ProjectItems.update_project_item/2` replaces `values` where `Projects.update_project_item/2`
202
merges them. Candidates for deletion. `Projects.create_project_field/1` also has no caller.
146 203
147 204
Left open, deliberately:
148 205

This page updates live while a promote is in flight · changelog