import { describe, expect, it } from "vitest";
import type { NetworkVouchItem, VouchKind } from "#lib/api/graph.ts";
import {
denounceStrength,
directVouch,
indirectVouches,
isDirectDenounce,
isDirectVouch,
isIndirectDenounce,
isIndirectVouch,
isMixed,
type VouchRelationship,
vouchBadgeState,
vouchStrength
} from "#lib/components/profile/vouchRelationship.ts";
const viewer = "did:plc:viewer";
const actor = "did:plc:actor";
const item = (author: string, subject: string, kind: VouchKind): NetworkVouchItem => ({
actor,
uri: `at://${author}/org.tangled.graph.vouch/${subject}`,
value: {
$type: "org.tangled.graph.vouch",
kind,
createdAt: "2026-01-01T00:00:00.000Z"
}
});
const relationship = (...vouches: NetworkVouchItem[]): VouchRelationship => ({
viewer,
actor,
vouches
});
describe("vouch relationship", () => {
it("lets a direct vouch beat mixed indirect evidence", () => {
const value = relationship(
item(viewer, actor, "vouch"),
item("did:plc:friend", actor, "vouch"),
item("did:plc:critic", actor, "denounce")
);
expect(isDirectVouch(value)).toBe(true);
expect(isDirectDenounce(value)).toBe(false);
expect(isMixed(value)).toBe(false);
expect(isIndirectVouch(value)).toBe(false);
expect(isIndirectDenounce(value)).toBe(false);
expect(vouchBadgeState(value)).toBe("direct-vouch");
});
it("derives mixed only from non-viewer incoming vouches", () => {
const value = relationship(
item("did:plc:friend", actor, "vouch"),
item("did:plc:critic", actor, "denounce"),
item(actor, "did:plc:friend", "vouch")
);
expect(indirectVouches(value)).toHaveLength(2);
expect(isMixed(value)).toBe(true);
expect(vouchStrength(value)).toBe(1);
expect(denounceStrength(value)).toBe(1);
expect(vouchBadgeState(value)).toBe("mixed");
});
it.each([
["direct-denounce", relationship(item(viewer, actor, "denounce"))],
["indirect-vouch", relationship(item("did:plc:friend", actor, "vouch"))],
["indirect-denounce", relationship(item("did:plc:critic", actor, "denounce"))]
] as const)("derives %s", (state, value) => {
expect(vouchBadgeState(value)).toBe(state);
});
it("reads the viewer's own vouch on the actor, with its reason", () => {
const own: NetworkVouchItem = {
actor,
uri: `at://${viewer}/org.tangled.graph.vouch/${actor}`,
value: {
$type: "org.tangled.graph.vouch",
kind: "denounce",
reason: "reason",
createdAt: "2026-01-01T00:00:00.000Z"
}
};
expect(directVouch(relationship(own, item("did:plc:friend", actor, "vouch")))).toEqual({
kind: "denounce",
reason: "reason"
});
expect(directVouch(relationship(item("did:plc:friend", actor, "vouch")))).toBeNull();
expect(directVouch(relationship(item(actor, viewer, "vouch")))).toBeNull();
});
it("ignores outgoing and unrelated vouches", () => {
const value = relationship(
item(actor, "did:plc:friend", "vouch"),
item("did:plc:friend", "did:plc:other", "denounce")
);
expect(vouchBadgeState(value)).toBeNull();
expect(vouchStrength(value)).toBe(0);
expect(denounceStrength(value)).toBe(0);
});
});