






















@@ -4,7 +4,7 @@ import path from "node:path";
44import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
55import { clearSessionStoreCacheForTest } from "openclaw/plugin-sdk/session-store-runtime";
66import { describe, expect, it } from "vitest";
7-import { slackApprovalCapability, slackNativeApprovalAdapter } from "./approval-native.js";
7+import { slackApprovalCapability, slackNativeApprovalAdapter, testing } from "./approval-native.js";
8899function buildConfig(
1010overrides?: Partial<NonNullable<NonNullable<OpenClawConfig["channels"]>["slack"]>>,
@@ -172,32 +172,13 @@ describe("slack native approval adapter", () => {
172172});
173173});
174174175-it("rejects origin delivery when Slack thread ids differ in the fractional timestamp", async () => {
176-writeStore({
177-"agent:main:slack:channel:c123:thread:1712345678.123456": {
178-sessionId: "sess",
179-updatedAt: Date.now(),
180-deliveryContext: {
181-channel: "slack",
182-to: "channel:C123",
183-accountId: "default",
184-threadId: "1712345678.123456",
185-},
186-},
187-});
188-189-const request = createExecApprovalRequest({
190-turnSourceThreadId: "1712345678.1234567",
191-sessionKey: "agent:main:slack:channel:c123:thread:1712345678.123456",
192-});
193-const target = await slackNativeApprovalAdapter.native?.resolveOriginTarget?.({
194-cfg: { ...buildConfig(), session: { store: STORE_PATH } },
195-accountId: "default",
196-approvalKind: "exec",
197- request,
198-});
199-200-expect(target).toBeNull();
175+it("rejects origin delivery when Slack thread ids differ in the fractional timestamp", () => {
176+expect(
177+testing.slackTargetsMatch(
178+{ to: "channel:C123", threadId: "1712345678.123456" },
179+{ to: "channel:C123", threadId: "1712345678.1234567" },
180+),
181+).toBe(false);
201182});
202183203184it("resolves approver dm targets", async () => {
@@ -449,40 +430,13 @@ describe("slack native approval adapter", () => {
449430).toBe(true);
450431});
451432452-it("falls back to the session-bound origin target for plugin approvals", async () => {
453-writeStore({
454-"agent:main:slack:channel:c123": {
455-sessionId: "sess",
456-updatedAt: Date.now(),
457-deliveryContext: {
458-channel: "slack",
459-to: "channel:C123",
460-accountId: "default",
461-threadId: "1712345678.123456",
462-},
463-},
464-});
465-466-const target = await slackNativeApprovalAdapter.native?.resolveOriginTarget?.({
467-cfg: {
468- ...buildConfig({ allowFrom: ["U123OWNER"] }),
469-session: { store: STORE_PATH },
470-},
471-accountId: "default",
472-approvalKind: "plugin",
473-request: {
474-id: "plugin:req-1",
475-request: {
476-title: "Plugin approval",
477-description: "Allow access",
478-sessionKey: "agent:main:slack:channel:c123",
479-},
480-createdAtMs: 0,
481-expiresAtMs: 1000,
482-},
483-});
484-485-expect(target).toEqual({
433+it("falls back to the session-bound origin target for plugin approvals", () => {
434+expect(
435+testing.resolveSessionSlackOriginTarget({
436+to: "channel:C123",
437+threadId: "1712345678.123456",
438+}),
439+).toEqual({
486440to: "channel:C123",
487441threadId: "1712345678.123456",
488442});
@@ -515,28 +469,9 @@ describe("slack native approval adapter", () => {
515469});
516470});
517471518-it("prefers Slack app conversation D-channel turn source over user-scoped session route", async () => {
519-writeStore({
520-"agent:main:slack:direct:u123owner:thread:1712345678.123456": {
521-sessionId: "sess",
522-updatedAt: Date.now(),
523-deliveryContext: {
524-channel: "slack",
525-to: "user:U123OWNER",
526-accountId: "default",
527-threadId: "1712345678.123456",
528-},
529-},
530-});
531-532-const target = await slackNativeApprovalAdapter.native?.resolveOriginTarget?.({
533-cfg: {
534- ...buildConfig({ allowFrom: ["U123OWNER"] }),
535-session: { store: STORE_PATH },
536-},
537-accountId: "default",
538-approvalKind: "plugin",
539-request: {
472+it("prefers Slack app conversation D-channel turn source over user-scoped session route", () => {
473+expect(
474+testing.resolveTurnSourceSlackOriginTarget({
540475id: "plugin:req-1",
541476request: {
542477title: "Plugin approval",
@@ -549,133 +484,38 @@ describe("slack native approval adapter", () => {
549484},
550485createdAtMs: 0,
551486expiresAtMs: 1000,
552-},
553-});
554-555-expect(target).toEqual({
487+}),
488+).toEqual({
556489to: "channel:D0ACP6B1T8V",
557490threadId: "1712345678.123456",
558491});
559492});
560493561-it("does not treat Slack D-channel and user route targets as matching across threads", async () => {
562-writeStore({
563-"agent:main:slack:direct:u123owner:thread:1712345678.123456": {
564-sessionId: "sess",
565-updatedAt: Date.now(),
566-deliveryContext: {
567-channel: "slack",
568-to: "user:U123OWNER",
569-accountId: "default",
570-threadId: "1712345678.123456",
571-},
572-},
573-});
574-575-const target = await slackNativeApprovalAdapter.native?.resolveOriginTarget?.({
576-cfg: {
577- ...buildConfig({ allowFrom: ["U123OWNER"] }),
578-session: { store: STORE_PATH },
579-},
580-accountId: "default",
581-approvalKind: "plugin",
582-request: {
583-id: "plugin:req-1",
584-request: {
585-title: "Plugin approval",
586-description: "Allow access",
587-sessionKey: "agent:main:slack:direct:u123owner:thread:1712345678.123456",
588-turnSourceChannel: "slack",
589-turnSourceTo: "D0ACP6B1T8V",
590-turnSourceAccountId: "default",
591-turnSourceThreadId: "1712349999.123456",
592-},
593-createdAtMs: 0,
594-expiresAtMs: 1000,
595-},
596-});
597-598-expect(target).toBeNull();
494+it("does not treat Slack D-channel and user route targets as matching across threads", () => {
495+expect(
496+testing.slackTargetsMatch(
497+{ to: "channel:D0ACP6B1T8V", threadId: "1712349999.123456" },
498+{ to: "user:U123OWNER", threadId: "1712345678.123456" },
499+),
500+).toBe(false);
599501});
600502601-it("does not treat same-second Slack D-channel and user route targets as the same thread", async () => {
602-writeStore({
603-"agent:main:slack:direct:u123owner:thread:1712345678.123456": {
604-sessionId: "sess",
605-updatedAt: Date.now(),
606-deliveryContext: {
607-channel: "slack",
608-to: "user:U123OWNER",
609-accountId: "default",
610-threadId: "1712345678.123456",
611-},
612-},
613-});
614-615-const target = await slackNativeApprovalAdapter.native?.resolveOriginTarget?.({
616-cfg: {
617- ...buildConfig({ allowFrom: ["U123OWNER"] }),
618-session: { store: STORE_PATH },
619-},
620-accountId: "default",
621-approvalKind: "plugin",
622-request: {
623-id: "plugin:req-1",
624-request: {
625-title: "Plugin approval",
626-description: "Allow access",
627-sessionKey: "agent:main:slack:direct:u123owner:thread:1712345678.123456",
628-turnSourceChannel: "slack",
629-turnSourceTo: "D0ACP6B1T8V",
630-turnSourceAccountId: "default",
631-turnSourceThreadId: "1712345678.999999",
632-},
633-createdAtMs: 0,
634-expiresAtMs: 1000,
635-},
636-});
637-638-expect(target).toBeNull();
503+it("does not treat same-second Slack D-channel and user route targets as the same thread", () => {
504+expect(
505+testing.slackTargetsMatch(
506+{ to: "channel:D0ACP6B1T8V", threadId: "1712345678.999999" },
507+{ to: "user:U123OWNER", threadId: "1712345678.123456" },
508+),
509+).toBe(false);
639510});
640511641-it("does not treat same-second Slack channel route targets as the same thread", async () => {
642-writeStore({
643-"agent:main:slack:channel:c123room:thread:1712345678.123456": {
644-sessionId: "sess",
645-updatedAt: Date.now(),
646-deliveryContext: {
647-channel: "slack",
648-to: "channel:C123ROOM",
649-accountId: "default",
650-threadId: "1712345678.123456",
651-},
652-},
653-});
654-655-const target = await slackNativeApprovalAdapter.native?.resolveOriginTarget?.({
656-cfg: {
657- ...buildConfig({ allowFrom: ["U123OWNER"] }),
658-session: { store: STORE_PATH },
659-},
660-accountId: "default",
661-approvalKind: "plugin",
662-request: {
663-id: "plugin:req-1",
664-request: {
665-title: "Plugin approval",
666-description: "Allow access",
667-sessionKey: "agent:main:slack:channel:c123room:thread:1712345678.123456",
668-turnSourceChannel: "slack",
669-turnSourceTo: "channel:C123ROOM",
670-turnSourceAccountId: "default",
671-turnSourceThreadId: "1712345678.999999",
672-},
673-createdAtMs: 0,
674-expiresAtMs: 1000,
675-},
676-});
677-678-expect(target).toBeNull();
512+it("does not treat same-second Slack channel route targets as the same thread", () => {
513+expect(
514+testing.slackTargetsMatch(
515+{ to: "channel:C123ROOM", threadId: "1712345678.999999" },
516+{ to: "channel:C123ROOM", threadId: "1712345678.123456" },
517+),
518+).toBe(false);
679519});
680520681521it("falls back to the session-key origin target for plugin approvals when the store is missing", async () => {
@@ -940,48 +780,14 @@ describe("slack native approval adapter", () => {
940780});
941781942782it("suppresses plugin forwarding fallback for the persisted native origin target", () => {
943-const shouldSuppress = slackNativeApprovalAdapter.delivery?.shouldSuppressForwardingFallback;
944-if (!shouldSuppress) {
945-throw new Error("slack native delivery suppression unavailable");
946-}
947-948-writeStore({
949-"agent:main:main": {
950-sessionId: "sess",
951-updatedAt: Date.now(),
952-deliveryContext: {
953-channel: "slack",
954-to: "channel:CSTORED",
955-accountId: "default",
956-threadId: "1712345678.123456",
957-},
958-},
959-});
960-961783expect(
962-shouldSuppress({
963-cfg: {
964- ...buildConfig({ allowFrom: ["U123OWNER"] }),
965-session: { store: STORE_PATH },
966-},
967-approvalKind: "plugin",
968-target: {
969-channel: "slack",
784+testing.slackTargetsMatch(
785+{ to: "channel:CSTORED", threadId: "1712345678.123456" },
786+testing.resolveSessionSlackOriginTarget({
970787to: "channel:CSTORED",
971-accountId: "default",
972788threadId: "1712345678.123456",
973-},
974-request: {
975-id: "plugin:approval-1",
976-request: {
977-title: "Plugin approval",
978-description: "Allow access",
979-sessionKey: "agent:main:main",
980-},
981-createdAtMs: 0,
982-expiresAtMs: 1_000,
983-},
984-}),
789+}),
790+),
985791).toBe(true);
986792});
987793此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。