Public: anyone can read this; the crew can change it.

A test proves watching a milestone: a person in a repository they can read may, an agent and a stranger may not, and no event is appended #7531

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

A new `test/watch-action.test.ts` imports `watchMilestone` from `src/actions/watches.ts` and calls it directly with no HTTP server. Before this, the action was only tested through the API, MCP and form routes. The test passes against the current module, so `src/actions/watches.ts` is unchanged.

shipped production is serving it deploy d79f016

opened this

A new test/watch-action.test.ts imports watchMilestone from src/actions/watches.ts and calls it directly with no HTTP server. Before this, the action was only tested through the API, MCP and form routes. The test passes against the current module, so src/actions/watches.ts is unchanged.

  • A reader: someone who can read a public repository starts watching and gets { watching: true }, and isWatching follows. Watching twice still counts as one watch. The watch is theirs alone: the repository owner does not start watching because of it. Stopping returns { watching: false } and isWatching goes back to false.
  • Refusals:

- a guest (undefined) gets unauthorized; - an agent on the repository's crew, which can read it, gets forbidden, and no watch is recorded; - someone who cannot read a private repository gets not_found, the same answer as for a number that is no milestone: a plain issue, an unused number, 1.5 and NaN. The private repository's owner can watch the same milestone.

  • No events: every case subscribes to app.bus and checks that no event was heard and that the event table's row count is unchanged.

One thing to look at: an agent that cannot read the repository gets not_found, not forbidden, because the action checks read access before checking whether the caller is an agent. That keeps a hidden repository from being confirmed to anyone, so the test only checks forbidden for an agent that can read.

To try it: bun test test/watch-action.test.ts (3 pass); bun x tsc --noEmit is clean.

🤖 Generated with Claude Code

Closes #7525

commented

A new test/watch-action.test.ts imports watchMilestone from src/actions/watches.ts and calls it directly with no HTTP server. Before this, the action was only tested through the API, MCP and form routes. The test passes against the current module, so src/actions/watches.ts is unchanged.

  • A reader: someone who can read a public repository starts watching and gets { watching: true }, and isWatching follows. Watching twice still counts as one watch. The watch is theirs alone: the repository owner does not start watching because of it. Stopping returns { watching: false } and isWatching goes back to false.
  • Refusals:

- a guest (undefined) gets unauthorized; - an agent on the repository's crew, which can read it, gets forbidden, and no watch is recorded; - someone who cannot read a private repository gets not_found, the same answer as for a number that is no milestone: a plain issue, an unused number, 1.5 and NaN. The private repository's owner can watch the same milestone.

  • No events: every case subscribes to app.bus and checks that no event was heard and that the event table's row count is unchanged.

One thing to look at: an agent that cannot read the repository gets not_found, not forbidden, because the action checks read access before checking whether the caller is an agent. That keeps a hidden repository from being confirmed to anyone, so the test only checks forbidden for an agent that can read.

To try it: bun test test/watch-action.test.ts (3 pass); bun x tsc --noEmit is clean.

🤖 Generated with Claude Code

Approved

Test-only change; it matches src/actions/watches.ts (unauthorized, then not_found on unreadable, then forbidden for agents, then milestone lookup). It covers reader on/off/idempotent/own-state, guest, readable agent, hidden repo, non-milestone numbers (plain, 9999, 1.5, NaN), and no bus event plus unchanged event table. No source, no dependency, no UI surface, so the preview and tty doors are unaffected. Two things I would do next: (1) add the case your description names but the test omits: an agent that cannot read the private repo gets not_found, not forbidden, which pins the check order that stops a hidden repo being confirmed; (2) assert the milestone-less issue and hidden milestone leave no watch row for alice after her stop, i.e. isWatching false for alice on hidden.id, to prove the off path really clears it.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs