mirror of
https://github.com/openclaw/openclaw.git
synced 2026-10-03 01:29:56 +00:00
fix(ci): run Telegram skill tests when a PR changes only skill test files (#163328)
The Telegram userbot skill's node:test (*.test.mjs) and Python (*.test.py) files are not Vitest-routable, so aggressive PR selection drops them as direct targets. Their only CI owner is the Vitest wrapper test/scripts/telegram-e2e-userbot-skill.test.ts, which the policy watch selected for 20 named non-test scripts only. Test-only skill PRs (e.g. #162968) and later scripts (reply-policy-*, telegram-runtime) planned nothing in either selection mode. Watch the skill's whole scripts/ directory, the agents/openai.yaml the wrapper parses, and the fixture it loads by URL. The wrapper stays in the release-only core-tooling family; it now also runs on PRs that touch the skill, the only PRs that can break these self-contained scripts.
This commit is contained in:
parent
d20ba7392c
commit
5ab66a5fe4
4 changed files with 59 additions and 35 deletions
|
|
@ -125,7 +125,10 @@ and scripts that the import graph cannot discover.
|
|||
Explicit policy watches retain their matching tests in PR CI even when the broader
|
||||
tooling or runtime suite is deferred. This includes wrapper dependency checks,
|
||||
Gateway client callsite scans, and upgrade-survivor package checks. Unrelated
|
||||
deferred tests stay excluded.
|
||||
deferred tests stay excluded. Changed test files that no Vitest config routes,
|
||||
such as skill `node:test` and Python suites, are never direct targets; the watch
|
||||
for their Vitest wrapper must cover those test files too, or test-only edits run
|
||||
in no PR job.
|
||||
|
||||
Aggressive PR selection keeps changed tests and direct runtime import consumers,
|
||||
including package and SDK aliases. A changed module with fewer than 20 direct
|
||||
|
|
|
|||
|
|
@ -1966,28 +1966,13 @@ const policyTestWatches: readonly PolicyTestWatch[] = [
|
|||
],
|
||||
},
|
||||
{
|
||||
// Sole CI owner of the skill's node:test and Python suites: no Vitest config
|
||||
// routes those files, so test-only skill edits must select this wrapper.
|
||||
testFile: "test/scripts/telegram-e2e-userbot-skill.test.ts",
|
||||
watchGlobs: [
|
||||
".agents/skills/telegram-e2e-userbot/scripts/followup-drain-control-preload.mjs",
|
||||
".agents/skills/telegram-e2e-userbot/scripts/published-upgrade-artifact.mjs",
|
||||
".agents/skills/telegram-e2e-userbot/scripts/published-upgrade-scenario.mjs",
|
||||
".agents/skills/telegram-e2e-userbot/scripts/qa-credential-lease.mjs",
|
||||
".agents/skills/telegram-e2e-userbot/scripts/run-mock-sut-user-e2e.mjs",
|
||||
".agents/skills/telegram-e2e-userbot/scripts/run-published-upgrade-user-e2e.mjs",
|
||||
".agents/skills/telegram-e2e-userbot/scripts/scenario.mjs",
|
||||
".agents/skills/telegram-e2e-userbot/scripts/telegram-api-ignore-abort-preload.mjs",
|
||||
".agents/skills/telegram-e2e-userbot/scripts/telegram-binding-checkpoint.mjs",
|
||||
".agents/skills/telegram-e2e-userbot/scripts/telegram-binding-forum.py",
|
||||
".agents/skills/telegram-e2e-userbot/scripts/telegram-binding-upgrade-verdict.mjs",
|
||||
".agents/skills/telegram-e2e-userbot/scripts/telegram-run-scope.mjs",
|
||||
".agents/skills/telegram-e2e-userbot/scripts/telegram-test-api-proxy.mjs",
|
||||
".agents/skills/telegram-e2e-userbot/scripts/telegram-test-credential.mjs",
|
||||
".agents/skills/telegram-e2e-userbot/scripts/telegram-test-doctor.mjs",
|
||||
".agents/skills/telegram-e2e-userbot/scripts/telegram-test-group.mjs",
|
||||
".agents/skills/telegram-e2e-userbot/scripts/telegram-test-recover.mjs",
|
||||
".agents/skills/telegram-e2e-userbot/scripts/triage-mock-openai.mjs",
|
||||
".agents/skills/telegram-e2e-userbot/scripts/user-driver.py",
|
||||
".agents/skills/telegram-e2e-userbot/scripts/user-record.py",
|
||||
".agents/skills/telegram-e2e-userbot/agents/openai.yaml",
|
||||
".agents/skills/telegram-e2e-userbot/scripts/**",
|
||||
"test/scripts/fixtures/triage-fixture-startup.mjs",
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -44,19 +44,29 @@ it.each(["added", "renamed", "deleted", "import-edge"])(
|
|||
},
|
||||
);
|
||||
|
||||
it("selects changed test files directly only on routable Vitest surfaces", () => {
|
||||
const cwd = tempDirs.make("node-selection-routable-");
|
||||
// Skill scripts carry node:test files that no Vitest config owns.
|
||||
const skillTest = ".agents/skills/example/scripts/driver.test.mjs";
|
||||
const routable = "src/infra/own.test.ts";
|
||||
for (const file of [skillTest, routable]) {
|
||||
mkdirSync(path.dirname(path.join(cwd, file)), { recursive: true });
|
||||
writeFileSync(path.join(cwd, file), "export {};\n");
|
||||
}
|
||||
expect(
|
||||
resolveChangedNodeTestTargets([skillTest, routable], { cwd, selectionMode: "aggressive" }),
|
||||
).toEqual([routable]);
|
||||
});
|
||||
it.each(["aggressive", "full"] as const)(
|
||||
"routes skill node:test and Python files through their Vitest owner in %s mode",
|
||||
(selectionMode) => {
|
||||
const cwd = tempDirs.make("node-selection-routable-");
|
||||
// Skill scripts carry node:test and Python files that no Vitest config owns.
|
||||
const unownedSkillTest = ".agents/skills/example/scripts/driver.test.mjs";
|
||||
const skillTest = ".agents/skills/telegram-e2e-userbot/scripts/driver.test.mjs";
|
||||
const skillPythonTest = ".agents/skills/telegram-e2e-userbot/scripts/driver.test.py";
|
||||
const wrapper = "test/scripts/telegram-e2e-userbot-skill.test.ts";
|
||||
const routable = "src/infra/own.test.ts";
|
||||
for (const file of [unownedSkillTest, skillTest, skillPythonTest, wrapper, routable]) {
|
||||
mkdirSync(path.dirname(path.join(cwd, file)), { recursive: true });
|
||||
writeFileSync(path.join(cwd, file), "export {};\n");
|
||||
}
|
||||
expect(resolveChangedNodeTestTargets([skillTest], { cwd, selectionMode })).toEqual([wrapper]);
|
||||
expect(
|
||||
resolveChangedNodeTestTargets([unownedSkillTest, skillPythonTest, routable], {
|
||||
cwd,
|
||||
selectionMode,
|
||||
}),
|
||||
).toEqual([routable, wrapper]);
|
||||
},
|
||||
);
|
||||
|
||||
it("bounds protected regressions to nearby consumers and restores area coverage in full mode", () => {
|
||||
const cwd = tempDirs.make("node-selection-");
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
||||
import { existsSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
|
||||
import { join, matchesGlob } from "node:path";
|
||||
import { expectDefined } from "@openclaw/normalization-core";
|
||||
import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from "vitest";
|
||||
|
|
@ -2055,6 +2055,32 @@ describe("scripts/lib/ci-node-test-plan.mts", () => {
|
|||
}
|
||||
});
|
||||
|
||||
it("runs Telegram skill script changes, including test-only edits, through the skill wrapper", () => {
|
||||
const wrapper = "test/scripts/telegram-e2e-userbot-skill.test.ts";
|
||||
const scriptsDir = ".agents/skills/telegram-e2e-userbot/scripts";
|
||||
const scripts = readdirSync(scriptsDir, { withFileTypes: true })
|
||||
.filter((entry) => entry.isFile() && !entry.name.startsWith("."))
|
||||
.map((entry) => `${scriptsDir}/${entry.name}`);
|
||||
expect(scripts.filter((file) => /\.test\.(?:mjs|py)$/u.test(file)).length).toBeGreaterThan(0);
|
||||
for (const changedPath of scripts) {
|
||||
expect(resolvePolicyTestTargets([changedPath]), changedPath).toContain(wrapper);
|
||||
}
|
||||
const changedTest = `${scriptsDir}/telegram-run-composition.test.mjs`;
|
||||
const shards = expectDefined(
|
||||
createChangedNodeTestShards([changedTest], { selectionMode: "aggressive" }),
|
||||
"skill test plan",
|
||||
);
|
||||
const groups = shards.flatMap((shard) => shard.groups ?? []);
|
||||
const selected = [
|
||||
...shards.flatMap((shard) => shard.targets ?? []),
|
||||
...groups.flatMap((group) => group.includePatterns ?? []),
|
||||
];
|
||||
expect(selected).not.toContain(changedTest);
|
||||
const owners = groups.filter((group) => group.includePatterns?.includes(wrapper));
|
||||
expect(owners).toHaveLength(1);
|
||||
expect(owners[0]?.configs).toEqual(["test/vitest/vitest.tooling.config.ts"]);
|
||||
});
|
||||
|
||||
it("matches policy owners with literal and native glob semantics", () => {
|
||||
const changedPath = "ui/src/styles/base.css";
|
||||
expect(isPolicyTestOwnedPath(changedPath)).toBe(true);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue