mirror of
https://github.com/block/buzz.git
synced 2026-11-08 02:11:36 +00:00
## Summary - adopt the finalized NIP-MP project model so one project can enumerate and switch between multiple NIP-34 repositories - add project and repository navigation, activity summaries, existing-repository attachment, and repository access-channel management - preserve privacy-safe activation provenance for agent-authored patches, pull requests, issues, and associated commits ## Test plan - [x] Run desktop typecheck and unit tests - [x] Run focused NIP-MP, repository access, and provenance tests - [x] Run Rust formatting and desktop lint checks - [x] Run the complete pre-push suite after merging current `main` - [ ] Manually verify project creation, repository attachment, switching, and access repair on staging - [ ] Manually verify public-channel and private-agent origin labels on newly created Git activity Related: [#4695](https://github.com/block/buzz/pull/4695) --------- Signed-off-by: Thomas Petersen <thomasp@squareup.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com> Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz> Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
140 lines
4.3 KiB
JavaScript
140 lines
4.3 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import test from "node:test";
|
|
|
|
import { fetchProjectsWorkItems } from "./projectWorkItems.ts";
|
|
|
|
// ── Work-item deduplication ─────────────────────────────────────────────────
|
|
//
|
|
// NIP-MP §Multiple membership: a repository may belong to several projects.
|
|
// When it does, global issue/PR lists must produce exactly one row per work
|
|
// item — not one row per project membership. These tests call the exported
|
|
// production function with a stubbed fetchEvents to verify the dedup contract
|
|
// end-to-end, not just the filter algorithm in isolation.
|
|
|
|
const REPO_OWNER = "a".repeat(64);
|
|
const REPO_DTAG = "relay";
|
|
const REPO_ADDRESS = `30617:${REPO_OWNER}:${REPO_DTAG}`;
|
|
|
|
const ISSUE_ID = "i".repeat(64);
|
|
const PR_ID = "p".repeat(64);
|
|
const PR_ID_2 = "q".repeat(64);
|
|
|
|
// Two projects that both contain the same repository.
|
|
const projectA = {
|
|
repositories: [{ repoAddress: REPO_ADDRESS }],
|
|
};
|
|
const projectB = {
|
|
repositories: [{ repoAddress: REPO_ADDRESS }],
|
|
};
|
|
|
|
// Minimal valid NIP-34 issue event for the shared repo.
|
|
function makeIssue(id, updatedAt = 100) {
|
|
return {
|
|
id,
|
|
kind: 1621,
|
|
pubkey: REPO_OWNER,
|
|
created_at: updatedAt,
|
|
content: "An issue",
|
|
tags: [
|
|
["a", REPO_ADDRESS],
|
|
["subject", "Fix the thing"],
|
|
],
|
|
};
|
|
}
|
|
|
|
// Minimal valid NIP-34 pull request event for the shared repo.
|
|
function makePR(id, updatedAt = 100) {
|
|
return {
|
|
id,
|
|
kind: 1618, // KIND_GIT_PULL_REQUEST
|
|
pubkey: REPO_OWNER,
|
|
created_at: updatedAt,
|
|
content: "A PR",
|
|
tags: [
|
|
["a", REPO_ADDRESS],
|
|
["subject", "Add a feature"],
|
|
],
|
|
};
|
|
}
|
|
|
|
// fetchEvents stub: returns the given root events (issues + PRs) and empty
|
|
// arrays for all other query kinds (updates, comments, statuses).
|
|
function makeFetchEvents(rootEvents) {
|
|
return async (filter) => {
|
|
const { kinds } = filter;
|
|
// Root issues (kind 1621) + PRs (kind 1618)
|
|
if (kinds?.includes(1621) || kinds?.includes(1618)) {
|
|
return rootEvents.filter((e) => kinds.includes(e.kind));
|
|
}
|
|
// Everything else (PR updates, comments, statuses) — empty
|
|
return [];
|
|
};
|
|
}
|
|
|
|
test("fetchProjectsWorkItems deduplicates issues from a shared repository", async () => {
|
|
const issue = makeIssue(ISSUE_ID);
|
|
const fetchEvents = makeFetchEvents([issue]);
|
|
|
|
const result = await fetchProjectsWorkItems(
|
|
[projectA, projectB],
|
|
fetchEvents,
|
|
);
|
|
|
|
assert.equal(
|
|
result.issues.items.length,
|
|
1,
|
|
"duplicate issue from shared repo must collapse to one row",
|
|
);
|
|
assert.equal(result.issues.items[0].issue.id, ISSUE_ID);
|
|
});
|
|
|
|
test("fetchProjectsWorkItems deduplicates pull requests from a shared repository", async () => {
|
|
const pr1 = makePR(PR_ID, 100);
|
|
const pr2 = makePR(PR_ID_2, 90);
|
|
const fetchEvents = makeFetchEvents([pr1, pr2]);
|
|
|
|
const result = await fetchProjectsWorkItems(
|
|
[projectA, projectB],
|
|
fetchEvents,
|
|
);
|
|
|
|
assert.equal(
|
|
result.pullRequests.items.length,
|
|
2,
|
|
"distinct PRs must survive dedup; only exact-id duplicates collapse",
|
|
);
|
|
const ids = result.pullRequests.items.map((item) => item.pullRequest.id);
|
|
assert.ok(ids.includes(PR_ID), "first PR must be present");
|
|
assert.ok(ids.includes(PR_ID_2), "second PR must be present");
|
|
});
|
|
|
|
test("fetchProjectsWorkItems returns a single row for a PR present in both project contexts", async () => {
|
|
// Same PR id returned twice (once per project's relay query).
|
|
const pr = makePR(PR_ID, 100);
|
|
// The stub returns the same event for every root query, simulating
|
|
// the relay returning the same PR for both projects' repo addresses.
|
|
let callCount = 0;
|
|
const fetchEvents = async (filter) => {
|
|
if (filter.kinds?.includes(1618)) {
|
|
callCount += 1;
|
|
return [pr];
|
|
}
|
|
return [];
|
|
};
|
|
|
|
const result = await fetchProjectsWorkItems(
|
|
[projectA, projectB],
|
|
fetchEvents,
|
|
);
|
|
|
|
// The relay was queried once per unique repo address — but even if it
|
|
// returned the same id twice across the two project contexts, dedupe fires.
|
|
assert.equal(
|
|
result.pullRequests.items.length,
|
|
1,
|
|
"same PR id appearing in both project contexts must produce one row",
|
|
);
|
|
// Sanity: the stub was actually called (proves we ran the production path).
|
|
assert.ok(callCount >= 1, "fetchEvents must have been called");
|
|
});
|