






























@@ -449,6 +449,195 @@ describe("mattermostPlugin", () => {
449449expect(options.replyToId).toBe("post-root");
450450});
451451452+it("routes filePath send actions through Mattermost media upload options", async () => {
453+const cfg = createMattermostTestConfig();
454+const mediaReadFile = vi.fn(async () => Buffer.from("report"));
455+456+await mattermostPlugin.actions?.handleAction?.(
457+createMattermostActionContext({
458+action: "send",
459+params: {
460+to: "channel:CHAN1",
461+message: "report",
462+filePath: "/tmp/workspace/report.md",
463+},
464+ cfg,
465+accountId: "default",
466+mediaLocalRoots: ["/tmp/workspace"],
467+ mediaReadFile,
468+}),
469+);
470+471+const options = expectSingleMattermostSend("channel:CHAN1", "report");
472+expect(options.mediaUrl).toBe("/tmp/workspace/report.md");
473+expect(options.mediaLocalRoots).toStrictEqual(["/tmp/workspace"]);
474+expect(options.mediaReadFile).toBe(mediaReadFile);
475+expect(options.requireMediaUpload).toBe(true);
476+});
477+478+it("preserves workspaceDir for relative filePath send actions", async () => {
479+const cfg = createMattermostTestConfig();
480+const mediaReadFile = vi.fn(async () => Buffer.from("report"));
481+482+await mattermostPlugin.actions?.handleAction?.(
483+createMattermostActionContext({
484+action: "send",
485+params: {
486+to: "channel:CHAN1",
487+message: "report",
488+filePath: "report.md",
489+},
490+ cfg,
491+accountId: "default",
492+mediaAccess: {
493+localRoots: ["/tmp/workspace"],
494+readFile: mediaReadFile,
495+workspaceDir: "/tmp/workspace",
496+},
497+}),
498+);
499+500+const options = expectSingleMattermostSend("channel:CHAN1", "report");
501+expect(options.mediaUrl).toBe("report.md");
502+expect(options.mediaLocalRoots).toStrictEqual(["/tmp/workspace"]);
503+expect(options.mediaReadFile).toBe(mediaReadFile);
504+expect(options.workspaceDir).toBe("/tmp/workspace");
505+expect(options.requireMediaUpload).toBe(true);
506+});
507+508+it("routes structured attachment send actions through Mattermost media upload options", async () => {
509+const cfg = createMattermostTestConfig();
510+511+await mattermostPlugin.actions?.handleAction?.(
512+createMattermostActionContext({
513+action: "send",
514+params: {
515+to: "channel:CHAN1",
516+message: "report",
517+attachments: [{ filePath: "/tmp/workspace/report.md" }],
518+},
519+ cfg,
520+accountId: "default",
521+mediaLocalRoots: ["/tmp/workspace"],
522+}),
523+);
524+525+const options = expectSingleMattermostSend("channel:CHAN1", "report");
526+expect(options.mediaUrl).toBe("/tmp/workspace/report.md");
527+expect(options.mediaLocalRoots).toStrictEqual(["/tmp/workspace"]);
528+expect(options.requireMediaUpload).toBe(true);
529+});
530+531+it("routes media_urls send actions through Mattermost media upload options", async () => {
532+const cfg = createMattermostTestConfig();
533+534+await mattermostPlugin.actions?.handleAction?.(
535+createMattermostActionContext({
536+action: "send",
537+params: {
538+to: "channel:CHAN1",
539+message: "report",
540+media_urls: ["/tmp/workspace/report.md"],
541+},
542+ cfg,
543+accountId: "default",
544+mediaLocalRoots: ["/tmp/workspace"],
545+}),
546+);
547+548+const options = expectSingleMattermostSend("channel:CHAN1", "report");
549+expect(options.mediaUrl).toBe("/tmp/workspace/report.md");
550+expect(options.mediaLocalRoots).toStrictEqual(["/tmp/workspace"]);
551+expect(options.requireMediaUpload).toBe(true);
552+});
553+554+it("preserves HTTP media send fallback behavior", async () => {
555+const cfg = createMattermostTestConfig();
556+557+await mattermostPlugin.actions?.handleAction?.(
558+createMattermostActionContext({
559+action: "send",
560+params: {
561+to: "channel:CHAN1",
562+message: "report",
563+mediaUrl: "https://example.com/report.md",
564+},
565+ cfg,
566+accountId: "default",
567+}),
568+);
569+570+const options = expectSingleMattermostSend("channel:CHAN1", "report");
571+expect(options.mediaUrl).toBe("https://example.com/report.md");
572+expect(options.requireMediaUpload).toBeUndefined();
573+});
574+575+it("rejects multiple Mattermost send attachments instead of dropping extras", async () => {
576+const cfg = createMattermostTestConfig();
577+578+await expect(
579+mattermostPlugin.actions?.handleAction?.(
580+createMattermostActionContext({
581+action: "send",
582+params: {
583+to: "channel:CHAN1",
584+message: "reports",
585+media_urls: ["/tmp/workspace/one.md", "/tmp/workspace/two.md"],
586+},
587+ cfg,
588+accountId: "default",
589+mediaLocalRoots: ["/tmp/workspace"],
590+}),
591+),
592+).rejects.toThrow("supports one attachment per message");
593+expect(sendMessageMattermostMock).not.toHaveBeenCalled();
594+});
595+596+it("rejects unsupported buffer-only Mattermost send attachments", async () => {
597+const cfg = createMattermostTestConfig();
598+599+await expect(
600+mattermostPlugin.actions?.handleAction?.(
601+createMattermostActionContext({
602+action: "send",
603+params: {
604+to: "channel:CHAN1",
605+message: "report",
606+buffer: "cmVwb3J0",
607+filename: "report.md",
608+},
609+ cfg,
610+accountId: "default",
611+}),
612+),
613+).rejects.toThrow("buffer/base64 payloads are not supported");
614+expect(sendMessageMattermostMock).not.toHaveBeenCalled();
615+});
616+617+it("rejects mixed supported and unsupported Mattermost send attachments", async () => {
618+const cfg = createMattermostTestConfig();
619+620+await expect(
621+mattermostPlugin.actions?.handleAction?.(
622+createMattermostActionContext({
623+action: "send",
624+params: {
625+to: "channel:CHAN1",
626+message: "report",
627+attachments: [
628+{ filePath: "/tmp/workspace/report.md" },
629+{ buffer: "cmVwb3J0", filename: "report-copy.md" },
630+],
631+},
632+ cfg,
633+accountId: "default",
634+mediaLocalRoots: ["/tmp/workspace"],
635+}),
636+),
637+).rejects.toThrow("buffer/base64 payloads are not supported");
638+expect(sendMessageMattermostMock).not.toHaveBeenCalled();
639+});
640+452641it("maps legacy presentation buttons without using interactive conversion", async () => {
453642const cfg = createMattermostTestConfig();
454643@@ -634,6 +823,50 @@ describe("mattermostPlugin", () => {
634823]);
635824});
636825826+it("requires upload success for local media on presentation button payloads", async () => {
827+const renderPresentation = requireMattermostRenderPresentation();
828+const sendPayload = requireMattermostSendPayload();
829+const cfg = createMattermostTestConfig();
830+const mediaReadFile = vi.fn(async () => Buffer.from("image"));
831+const presentation = {
832+blocks: [
833+{
834+type: "buttons" as const,
835+buttons: [{ label: "Open", value: "open" }],
836+},
837+],
838+};
839+const rendered = await renderPresentation({
840+payload: { presentation, mediaUrl: "report.png" },
841+ presentation,
842+ctx: {
843+ cfg,
844+to: "channel:CHAN1",
845+text: "",
846+payload: { presentation, mediaUrl: "report.png" },
847+},
848+});
849+850+await sendPayload({
851+ cfg,
852+to: "channel:CHAN1",
853+text: "",
854+payload: rendered!,
855+mediaAccess: {
856+localRoots: ["/tmp/workspace"],
857+readFile: mediaReadFile,
858+workspaceDir: "/tmp/workspace",
859+},
860+});
861+862+const options = expectSingleMattermostSend("channel:CHAN1", "- Open");
863+expect(options.mediaUrl).toBe("report.png");
864+expect(options.mediaLocalRoots).toStrictEqual(["/tmp/workspace"]);
865+expect(options.mediaReadFile).toBe(mediaReadFile);
866+expect(options.workspaceDir).toBe("/tmp/workspace");
867+expect(options.requireMediaUpload).toBe(true);
868+});
869+637870it("keeps multi-media presentation payloads on the text/media fallback path", async () => {
638871const renderPresentation = requireMattermostRenderPresentation();
639872const presentation = {
@@ -671,13 +904,15 @@ describe("mattermostPlugin", () => {
671904it("forwards mediaLocalRoots on sendMedia", async () => {
672905const sendMedia = requireMattermostSendMedia();
673906const cfg = createMattermostTestConfig();
907+const mediaReadFile = vi.fn(async () => Buffer.from("image"));
674908675909const params: MattermostSendMediaParams = {
676910 cfg,
677911to: "channel:CHAN1",
678912text: "hello",
679913mediaUrl: "/tmp/workspace/image.png",
680914mediaLocalRoots: ["/tmp/workspace"],
915+ mediaReadFile,
681916accountId: "default",
682917replyToId: "post-root",
683918};
@@ -687,6 +922,33 @@ describe("mattermostPlugin", () => {
687922const options = expectSingleMattermostSend("channel:CHAN1", "hello");
688923expect(options.mediaUrl).toBe("/tmp/workspace/image.png");
689924expect(options.mediaLocalRoots).toStrictEqual(["/tmp/workspace"]);
925+expect(options.mediaReadFile).toBe(mediaReadFile);
926+expect(options.requireMediaUpload).toBe(true);
927+});
928+929+it("falls back to structured mediaAccess on sendMedia", async () => {
930+const sendMedia = requireMattermostSendMedia();
931+const cfg = createMattermostTestConfig();
932+const mediaReadFile = vi.fn(async () => Buffer.from("image"));
933+934+await sendMedia({
935+ cfg,
936+to: "channel:CHAN1",
937+text: "hello",
938+mediaUrl: "image.png",
939+mediaAccess: {
940+localRoots: ["/tmp/workspace"],
941+readFile: mediaReadFile,
942+workspaceDir: "/tmp/workspace",
943+},
944+});
945+946+const options = expectSingleMattermostSend("channel:CHAN1", "hello");
947+expect(options.mediaUrl).toBe("image.png");
948+expect(options.mediaLocalRoots).toStrictEqual(["/tmp/workspace"]);
949+expect(options.mediaReadFile).toBe(mediaReadFile);
950+expect(options.workspaceDir).toBe("/tmp/workspace");
951+expect(options.requireMediaUpload).toBe(true);
690952});
691953692954it("threads resolved cfg on sendText", async () => {
@@ -751,6 +1013,7 @@ describe("mattermostPlugin", () => {
7511013const options = expectSingleMattermostSend("channel:CHAN1", "caption");
7521014expect(options.accountId).toBe("default");
7531015expect(options.replyToId).toBe("post-root");
1016+expect(options.requireMediaUpload).toBeUndefined();
7541017});
7551018});
7561019此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。