mirror of
https://github.com/profullstack/logicsrc.git
synced 2026-10-03 05:07:10 +00:00
@logicsrc/plugin-agentmail provides read/search/compose/send/flag/delete over an injected MailTransport, gated to Founding Lifetime (paid) members. Returns plain JSON-serializable domain objects so the same API serves a human TUI, the CLI, MCP, and bots. - domain.ts: transport-agnostic types + pure helpers (parse/format address, normalizeDraft, isValidEmail, snippet) - ports.ts: MailTransport seam - service.ts: AgentMailService (inbox/list/read/search/send/reply/flag/delete) - access.ts: paid-member gate (assertPaid) + capability constants - transports/memory.ts: complete in-memory backend (tests/dev/reference) - transports/mailu.ts: self-hosted Mailu seam (mail.profullstack.com IMAP + smtp.profullstack.com submission) with injected IMAP/SMTP drivers - index.ts: PluginDefinition (manifest, routes, capabilities, tuiPanels) - registered in the root build; 20 vitest tests pass Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
63 lines
2.3 KiB
TypeScript
63 lines
2.3 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { DraftError, formatAddress, isValidEmail, mailboxAddress, normalizeDraft, parseAddress, snippet } from "./domain.js";
|
|
|
|
describe("isValidEmail", () => {
|
|
it("accepts normal addresses", () => {
|
|
expect(isValidEmail("alice@mail.profullstack.com")).toBe(true);
|
|
});
|
|
it("rejects malformed addresses", () => {
|
|
for (const bad of ["", "alice", "alice@", "@host.com", "a b@host.com", "alice@host"]) {
|
|
expect(isValidEmail(bad)).toBe(false);
|
|
}
|
|
});
|
|
});
|
|
|
|
describe("mailboxAddress", () => {
|
|
it("joins local part and domain", () => {
|
|
expect(mailboxAddress("alice", "mail.profullstack.com")).toBe("alice@mail.profullstack.com");
|
|
});
|
|
});
|
|
|
|
describe("parseAddress / formatAddress", () => {
|
|
it("parses a named address", () => {
|
|
expect(parseAddress("Ada Lovelace <ada@mail.profullstack.com>")).toEqual({
|
|
name: "Ada Lovelace",
|
|
address: "ada@mail.profullstack.com"
|
|
});
|
|
});
|
|
it("parses a bare address", () => {
|
|
expect(parseAddress("ada@mail.profullstack.com")).toEqual({ address: "ada@mail.profullstack.com" });
|
|
});
|
|
it("round-trips and quotes names with specials", () => {
|
|
expect(formatAddress({ name: "Ada", address: "ada@x.com" })).toBe("Ada <ada@x.com>");
|
|
expect(formatAddress({ address: "ada@x.com" })).toBe("ada@x.com");
|
|
expect(formatAddress({ name: "Doe, John", address: "j@x.com" })).toBe('"Doe, John" <j@x.com>');
|
|
});
|
|
});
|
|
|
|
describe("snippet", () => {
|
|
it("collapses whitespace and truncates", () => {
|
|
expect(snippet("hello world\n\nthere")).toBe("hello world there");
|
|
expect(snippet("abcdef", 4)).toBe("abc…");
|
|
});
|
|
});
|
|
|
|
describe("normalizeDraft", () => {
|
|
it("trims subject, drops empty cc/bcc, keeps valid recipients", () => {
|
|
const d = normalizeDraft({
|
|
to: [{ address: " ada@x.com " }],
|
|
cc: [],
|
|
subject: " hi ",
|
|
text: "yo"
|
|
});
|
|
expect(d.to).toEqual([{ address: "ada@x.com" }]);
|
|
expect(d.subject).toBe("hi");
|
|
expect(d.cc).toBeUndefined();
|
|
});
|
|
it("rejects a draft with no recipients", () => {
|
|
expect(() => normalizeDraft({ to: [], subject: "x", text: "y" })).toThrow(DraftError);
|
|
});
|
|
it("rejects an invalid recipient", () => {
|
|
expect(() => normalizeDraft({ to: [{ address: "nope" }], subject: "x", text: "y" })).toThrow(/invalid recipient/);
|
|
});
|
|
});
|