mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-28 05:16:23 -06:00
test(slack): align prerelease contracts (#105214)
* test(slack): align prerelease contracts * docs(agents): note review artifact enums * style(slack): format prerelease test
This commit is contained in:
committed by
GitHub
parent
b4a0fc5c57
commit
b02d276bbc
@@ -131,6 +131,8 @@ Skills own workflows; root owns hard policy and routing.
|
||||
|
||||
- Use `$openclaw-testing` for test/CI choice and `$crabbox` for remote/full/E2E proof.
|
||||
- At task start, if code changes, tests, builds, typechecks, lint fan-out, Docker, packaging, E2E, or live proof are likely, classify source trust and immediately pre-warm the safe Crabbox backend in a background command session. Trusted maintainer code defaults to Blacksmith Testbox; untrusted contributor/fork code uses secretless fork CI or sanitized direct AWS Crabbox under the rule above. Continue inspection/editing while it hydrates; sync the current checkout for every run, reuse the lease, then stop it before handoff.
|
||||
- Warm Testbox from the task checkout; lease ownership is checkout-path scoped.
|
||||
- PR review artifacts: keep template enum values; put evidence detail in summaries.
|
||||
- Crabbox request means real scenario proof: install/update/call/repro user path; not just copy tests and run them remotely.
|
||||
- Visual proof: use Crabbox, set up like a user, then screenshot-verify. No harness/bypass/shortcut unless explicitly asked.
|
||||
- Local agent work is limited to lightweight non-test checks such as `git diff --check`, targeted formatting, and cheap static probes. Tests and computationally intensive work default to the selected remote box.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{"seq":1,"at":0,"dir":"in","kind":"reply-start"}
|
||||
{"seq":2,"at":0,"dir":"out","kind":"assistant.threads.setStatus","data":{"payload":{"channel_id":"C0TRACE","loading_messages":["Reading the thread...","Checking context...","Working through the request...","Putting it all together..."],"status":"is typing...","thread_ts":"ts#1"},"result":{"ok":true},"target":"C0TRACE/ts#1"}}
|
||||
{"seq":3,"at":0,"dir":"in","kind":"final","data":{"text":"Release 2026.1.0 is ready to ship."}}
|
||||
{"seq":4,"at":0,"dir":"out","kind":"chat.postMessage","data":{"payload":{"blocks":[{"text":{"text":"Release 2026.1.0 is ready to ship.","type":"mrkdwn","verbatim":true},"type":"section"},{"block_id":"openclaw_reply_buttons_1","elements":[{"action_id":"openclaw:reply_button:1:1","text":{"emoji":true,"text":"Approve release","type":"plain_text"},"type":"button","value":"approve-release"},{"action_id":"openclaw:reply_link:1:2","text":{"emoji":true,"text":"Release notes","type":"plain_text"},"type":"button","url":"https://docs.openclaw.ai/release"}],"type":"actions"}],"channel":"C0TRACE","mrkdwn":false,"text":"Release 2026.1.0 is ready to ship.\n\nApprove release\nRelease notes","thread_ts":"ts#1","unfurl_links":false},"result":{"ts":"ts#2"},"target":"C0TRACE"}}
|
||||
{"seq":4,"at":0,"dir":"out","kind":"chat.postMessage","data":{"payload":{"blocks":[{"text":{"text":"Release 2026.1.0 is ready to ship.","type":"mrkdwn","verbatim":true},"type":"section"},{"block_id":"openclaw_reply_buttons_1","elements":[{"action_id":"openclaw:callback_button:1:1","text":{"emoji":true,"text":"Approve release","type":"plain_text"},"type":"button","value":"approve-release"},{"action_id":"openclaw:reply_link:1:2","text":{"emoji":true,"text":"Release notes","type":"plain_text"},"type":"button","url":"https://docs.openclaw.ai/release"}],"type":"actions"}],"channel":"C0TRACE","mrkdwn":false,"text":"Release 2026.1.0 is ready to ship.\n\nApprove release\nRelease notes","thread_ts":"ts#1","unfurl_links":false},"result":{"ts":"ts#2"},"target":"C0TRACE"}}
|
||||
{"seq":5,"at":0,"dir":"in","kind":"idle"}
|
||||
{"seq":6,"at":0,"dir":"out","kind":"assistant.threads.setStatus","data":{"payload":{"channel_id":"C0TRACE","status":"","thread_ts":"ts#1"},"result":{"ok":true},"target":"C0TRACE/ts#1"}}
|
||||
|
||||
@@ -718,58 +718,16 @@ describe("Slack native command argument menus", () => {
|
||||
);
|
||||
|
||||
it("falls back to static menus when app.options() throws during registration", async () => {
|
||||
const commands = new Map<string, (args: unknown) => Promise<void>>();
|
||||
const actions = new Map<string | RegExp, (args: unknown) => Promise<void>>();
|
||||
const postEphemeral = vi.fn().mockResolvedValue({ ok: true });
|
||||
const testHarness = createArgMenusHarness();
|
||||
const runtimeLog = vi.fn();
|
||||
const app = {
|
||||
client: { chat: { postEphemeral } },
|
||||
command: (name: string, handler: (args: unknown) => Promise<void>) => {
|
||||
commands.set(name, handler);
|
||||
},
|
||||
action: (id: string | RegExp, handler: (args: unknown) => Promise<void>) => {
|
||||
actions.set(id, handler);
|
||||
},
|
||||
// Simulate Bolt throwing during options registration (e.g. receiver not initialized)
|
||||
options: () => {
|
||||
throw new Error("Cannot read properties of undefined (reading 'listeners')");
|
||||
},
|
||||
(testHarness.ctx as { runtime: { log: typeof runtimeLog } }).runtime = { log: runtimeLog };
|
||||
testHarness.app.options = () => {
|
||||
throw new Error("Cannot read properties of undefined (reading 'listeners')");
|
||||
};
|
||||
const ctx = {
|
||||
cfg: { commands: { native: true, nativeSkills: false } },
|
||||
runtime: { log: runtimeLog },
|
||||
botToken: "bot-token",
|
||||
botUserId: "bot",
|
||||
teamId: "T1",
|
||||
allowFrom: ["*"],
|
||||
dmEnabled: true,
|
||||
dmPolicy: "open",
|
||||
groupDmEnabled: false,
|
||||
groupDmChannels: [],
|
||||
defaultRequireMention: true,
|
||||
groupPolicy: "open",
|
||||
useAccessGroups: false,
|
||||
channelsConfig: undefined,
|
||||
slashCommand: {
|
||||
enabled: true,
|
||||
name: "openclaw",
|
||||
ephemeral: true,
|
||||
sessionPrefix: "slack:slash",
|
||||
},
|
||||
textLimit: 4000,
|
||||
app,
|
||||
isChannelAllowed: () => true,
|
||||
resolveChannelName: async () => ({ name: "dm", type: "im" }),
|
||||
resolveUserName: async () => ({ name: "Ada" }),
|
||||
} as unknown;
|
||||
const account = {
|
||||
accountId: "acct",
|
||||
config: { commands: { native: true, nativeSkills: false } },
|
||||
} as unknown;
|
||||
|
||||
// Registration should not throw despite app.options() throwing
|
||||
await registerCommands(ctx, account);
|
||||
expect(commands.size).toBeGreaterThan(0);
|
||||
await registerCommands(testHarness.ctx, testHarness.account);
|
||||
expect(testHarness.commands.size).toBeGreaterThan(0);
|
||||
expect(runtimeLog).toHaveBeenCalledTimes(1);
|
||||
expect(runtimeLog).toHaveBeenCalledWith(
|
||||
expect.stringContaining(
|
||||
@@ -777,14 +735,14 @@ describe("Slack native command argument menus", () => {
|
||||
),
|
||||
);
|
||||
expect(
|
||||
Array.from(actions.keys()).some(
|
||||
Array.from(testHarness.actions.keys()).some(
|
||||
(key) => key instanceof RegExp && String(key) === String(/^openclaw_cmdarg/),
|
||||
),
|
||||
).toBe(true);
|
||||
|
||||
// The /reportexternal command (140 choices) should fall back to static_select
|
||||
// instead of external_select since options registration failed
|
||||
const handler = requireHandler(commands, "/reportexternal", "/reportexternal");
|
||||
const handler = requireHandler(testHarness.commands, "/reportexternal", "/reportexternal");
|
||||
const respond = vi.fn().mockResolvedValue(undefined);
|
||||
const ack = vi.fn().mockResolvedValue(undefined);
|
||||
await handler({
|
||||
|
||||
Reference in New Issue
Block a user