mirror of
https://gh-proxy.org/https://github.com/klinsmaya/multica.git
synced 2026-11-08 01:16:45 +08:00
Deleting a comment removed every reply under it, through the parent_id ON DELETE CASCADE. Now delete removes only that comment: one with replies becomes a tombstone (deleted_at set, body/attachments/reactions/resolution cleared) so the replies keep their parent, and one without replies is removed along with any tombstone ancestor left empty.
Tombstones are read-only and are never agent input. Every write to a comment's children takes its owners first (issue -> comment -> child), and a comment commits together with the attachments it was posted with. Clients only promise that replies are kept when the server declares comment_delete_keep_replies_supported, and then delete through DELETE /api/comments/{id}/keep-replies, which older servers do not route.
41 lines
1.5 KiB
TypeScript
41 lines
1.5 KiB
TypeScript
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
|
|
import { api } from "./api";
|
|
|
|
// api.ts refuses to load without a base URL; set it before the import runs.
|
|
vi.hoisted(() => {
|
|
process.env.EXPO_PUBLIC_API_URL = "https://api.example.test";
|
|
});
|
|
|
|
// The real store pulls in expo-secure-store; the client only needs the slug.
|
|
vi.mock("@/data/workspace-store", () => ({ getCurrentSlug: () => null }));
|
|
|
|
describe("api.deleteComment", () => {
|
|
const fetchMock = vi.fn(async () => new Response(null, { status: 204 }));
|
|
|
|
beforeEach(() => {
|
|
fetchMock.mockClear();
|
|
vi.stubGlobal("fetch", fetchMock);
|
|
vi.spyOn(console, "log").mockImplementation(() => {});
|
|
});
|
|
|
|
afterEach(() => {
|
|
vi.unstubAllGlobals();
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
// #8296: servers that keep a deleted comment's replies also route
|
|
// /keep-replies; older servers do not, so a keep-replies delete that reaches
|
|
// one fails instead of deleting the replies too.
|
|
it.each([
|
|
[{ keepReplies: true }, "https://api.example.test/api/comments/comment-1/keep-replies"],
|
|
[{ keepReplies: false }, "https://api.example.test/api/comments/comment-1"],
|
|
[undefined, "https://api.example.test/api/comments/comment-1"],
|
|
])("with %j sends DELETE %s", async (opts, url) => {
|
|
await api.deleteComment("comment-1", opts);
|
|
|
|
expect(fetchMock).toHaveBeenCalledTimes(1);
|
|
expect(fetchMock).toHaveBeenCalledWith(url, expect.objectContaining({ method: "DELETE" }));
|
|
});
|
|
});
|