A received Delete is recorded by Mastodon as a permanent tombstone — later Creates for the same URI are silently refused forever. The old code sent Delete on every non-public save 'just in case' (the comment even called over-sending harmless); on the 2026-06-07 soak this tombstoned an unlisted activity's URI before its first real publish, making its later flip to public invisible on the remote with no error anywhere. - visibilityTransitionAction(previous, next): Create on any transition to public (re-publish doubles as back-delivery; remotes dedupe by id), Delete only when leaving public, nothing for non-public→non-public. - updateActivityVisibility reads the previous visibility and acts on the transition. - design.md documents the tombstone permanence + the user-facing consequence (un-publish then re-publish won't resurrect the post on remotes that processed the retraction — same as Mastodon's own delete-and-redraft). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
28 lines
1.3 KiB
TypeScript
28 lines
1.3 KiB
TypeScript
import { describe, it, expect, vi } from "vitest";
|
|
|
|
// The db/boss imports in the module under test are irrelevant for the
|
|
// pure transition helper; stub them so importing the module is cheap.
|
|
vi.mock("./db.ts", () => ({ getDb: () => ({}) }));
|
|
vi.mock("./boss.server.ts", () => ({ enqueueOptional: vi.fn() }));
|
|
|
|
const { visibilityTransitionAction } = await import("./federation-delivery.server.ts");
|
|
|
|
describe("visibilityTransitionAction", () => {
|
|
it("publishes on any transition to public (including re-publish)", () => {
|
|
expect(visibilityTransitionAction("private", "public")).toBe("create");
|
|
expect(visibilityTransitionAction("unlisted", "public")).toBe("create");
|
|
expect(visibilityTransitionAction("public", "public")).toBe("create");
|
|
});
|
|
|
|
it("retracts only when leaving public", () => {
|
|
expect(visibilityTransitionAction("public", "private")).toBe("delete");
|
|
expect(visibilityTransitionAction("public", "unlisted")).toBe("delete");
|
|
});
|
|
|
|
it("stays silent for non-public to non-public — a Delete would tombstone the URI", () => {
|
|
expect(visibilityTransitionAction("private", "unlisted")).toBeNull();
|
|
expect(visibilityTransitionAction("unlisted", "private")).toBeNull();
|
|
expect(visibilityTransitionAction("private", "private")).toBeNull();
|
|
expect(visibilityTransitionAction("unlisted", "unlisted")).toBeNull();
|
|
});
|
|
});
|