Compare commits
1 Commits
main
...
feature/mu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
64d414ae27 |
31
.github/workflows/ci.yml
vendored
31
.github/workflows/ci.yml
vendored
@ -85,6 +85,8 @@ jobs:
|
||||
outputs:
|
||||
has_changed_extensions: ${{ steps.changed.outputs.has_changed_extensions }}
|
||||
changed_extensions_matrix: ${{ steps.changed.outputs.changed_extensions_matrix }}
|
||||
has_changed_fast_extensions: ${{ steps.changed.outputs.has_changed_fast_extensions }}
|
||||
changed_fast_extensions_matrix: ${{ steps.changed.outputs.changed_fast_extensions_matrix }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
@ -113,13 +115,22 @@ jobs:
|
||||
run: |
|
||||
node --input-type=module <<'EOF'
|
||||
import { appendFileSync } from "node:fs";
|
||||
import { listChangedExtensionIds } from "./scripts/test-extension.mjs";
|
||||
import {
|
||||
filterMultipassFastExtensionIds,
|
||||
listChangedExtensionIds,
|
||||
} from "./scripts/test-extension.mjs";
|
||||
|
||||
const extensionIds = listChangedExtensionIds({ base: process.env.BASE_SHA, head: "HEAD" });
|
||||
const fastExtensionIds = filterMultipassFastExtensionIds(extensionIds);
|
||||
const matrix = JSON.stringify({ include: extensionIds.map((extension) => ({ extension })) });
|
||||
const fastMatrix = JSON.stringify({
|
||||
include: fastExtensionIds.map((extension) => ({ extension })),
|
||||
});
|
||||
|
||||
appendFileSync(process.env.GITHUB_OUTPUT, `has_changed_extensions=${extensionIds.length > 0}\n`, "utf8");
|
||||
appendFileSync(process.env.GITHUB_OUTPUT, `changed_extensions_matrix=${matrix}\n`, "utf8");
|
||||
appendFileSync(process.env.GITHUB_OUTPUT, `has_changed_fast_extensions=${fastExtensionIds.length > 0}\n`, "utf8");
|
||||
appendFileSync(process.env.GITHUB_OUTPUT, `changed_fast_extensions_matrix=${fastMatrix}\n`, "utf8");
|
||||
EOF
|
||||
|
||||
# Build dist once for Node-relevant changes and share it with downstream jobs.
|
||||
@ -255,11 +266,11 @@ jobs:
|
||||
extension-fast:
|
||||
name: "extension-fast (${{ matrix.extension }})"
|
||||
needs: [docs-scope, changed-scope, changed-extensions]
|
||||
if: needs.docs-scope.outputs.docs_only != 'true' && needs.changed-scope.outputs.run_node == 'true' && needs.changed-extensions.outputs.has_changed_extensions == 'true'
|
||||
if: needs.docs-scope.outputs.docs_only != 'true' && needs.changed-scope.outputs.run_node == 'true' && needs.changed-extensions.outputs.has_changed_fast_extensions == 'true'
|
||||
runs-on: blacksmith-16vcpu-ubuntu-2404
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix: ${{ fromJson(needs.changed-extensions.outputs.changed_extensions_matrix) }}
|
||||
matrix: ${{ fromJson(needs.changed-extensions.outputs.changed_fast_extensions_matrix) }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
@ -272,10 +283,20 @@ jobs:
|
||||
install-bun: "false"
|
||||
use-sticky-disk: "false"
|
||||
|
||||
- name: Run changed extension tests
|
||||
- name: Checkout multipass
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
repository: openclaw/multipass
|
||||
path: .ci/multipass
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Install multipass dependencies
|
||||
run: pnpm --dir ./.ci/multipass install --frozen-lockfile
|
||||
|
||||
- name: Run multipass fast extension roundtrip
|
||||
env:
|
||||
OPENCLAW_CHANGED_EXTENSION: ${{ matrix.extension }}
|
||||
run: pnpm test:extension "$OPENCLAW_CHANGED_EXTENSION"
|
||||
run: node scripts/e2e/run-multipass-extension-fast.mjs --extension "$OPENCLAW_CHANGED_EXTENSION" --multipass-dir ./.ci/multipass
|
||||
|
||||
# Types, lint, and format check.
|
||||
check:
|
||||
|
||||
130
scripts/e2e/extension-fast-channel-smoke.mjs
Normal file
130
scripts/e2e/extension-fast-channel-smoke.mjs
Normal file
@ -0,0 +1,130 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { existsSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath, pathToFileURL } from "node:url";
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
const repoRoot = path.resolve(__dirname, "..", "..");
|
||||
|
||||
const CHANNEL_FAST_TEST_FILES = Object.freeze({
|
||||
discord: ["extensions/discord/src/channel.test.ts"],
|
||||
imessage: ["extensions/imessage/src/channel.outbound.test.ts"],
|
||||
line: ["src/line/webhook-node.test.ts"],
|
||||
signal: ["extensions/signal/src/channel.test.ts"],
|
||||
slack: ["extensions/slack/src/channel.test.ts"],
|
||||
telegram: ["extensions/telegram/src/channel.test.ts"],
|
||||
});
|
||||
|
||||
export function listFastChannelExtensions() {
|
||||
return Object.keys(CHANNEL_FAST_TEST_FILES).toSorted((left, right) => left.localeCompare(right));
|
||||
}
|
||||
|
||||
export function resolveFastChannelTestFiles(extension) {
|
||||
return CHANNEL_FAST_TEST_FILES[extension] ?? null;
|
||||
}
|
||||
|
||||
function parseArgs(argv) {
|
||||
let extension = "";
|
||||
let probeOnly = false;
|
||||
let listOnly = false;
|
||||
for (let index = 0; index < argv.length; index += 1) {
|
||||
const arg = argv[index];
|
||||
if (arg === "--extension") {
|
||||
extension = argv[index + 1] ?? "";
|
||||
index += 1;
|
||||
continue;
|
||||
}
|
||||
if (arg === "--probe") {
|
||||
probeOnly = true;
|
||||
continue;
|
||||
}
|
||||
if (arg === "--list") {
|
||||
listOnly = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return { extension, probeOnly, listOnly };
|
||||
}
|
||||
|
||||
function printUsageAndExit(message) {
|
||||
if (message) {
|
||||
process.stderr.write(`${message}\n`);
|
||||
}
|
||||
process.stderr.write(
|
||||
"Usage: node scripts/e2e/extension-fast-channel-smoke.mjs --extension <discord|imessage|line|signal|slack|telegram> [--probe]\n",
|
||||
);
|
||||
process.stderr.write(" node scripts/e2e/extension-fast-channel-smoke.mjs --list\n");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
function assertFilesExist(files) {
|
||||
for (const relativeFile of files) {
|
||||
const absoluteFile = path.resolve(repoRoot, relativeFile);
|
||||
if (!existsSync(absoluteFile)) {
|
||||
throw new Error(`Missing fast smoke file: ${relativeFile}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function runFastChannelSmoke(extension) {
|
||||
const files = resolveFastChannelTestFiles(extension);
|
||||
if (!files) {
|
||||
throw new Error(
|
||||
`Unsupported fast extension "${extension}". Expected one of: ${listFastChannelExtensions().join(", ")}`,
|
||||
);
|
||||
}
|
||||
assertFilesExist(files);
|
||||
|
||||
const result = spawnSync(
|
||||
"pnpm",
|
||||
["exec", "vitest", "run", "--config", "vitest.channels.config.ts", ...files],
|
||||
{
|
||||
cwd: repoRoot,
|
||||
stdio: "inherit",
|
||||
shell: process.platform === "win32",
|
||||
env: process.env,
|
||||
},
|
||||
);
|
||||
if (typeof result.status === "number") {
|
||||
return result.status;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
function main() {
|
||||
const { extension, probeOnly, listOnly } = parseArgs(process.argv.slice(2));
|
||||
|
||||
if (listOnly) {
|
||||
process.stdout.write(`${listFastChannelExtensions().join("\n")}\n`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!extension) {
|
||||
printUsageAndExit("Missing required --extension argument.");
|
||||
}
|
||||
|
||||
const files = resolveFastChannelTestFiles(extension);
|
||||
if (!files) {
|
||||
printUsageAndExit(
|
||||
`Unsupported extension "${extension}". Expected one of: ${listFastChannelExtensions().join(", ")}`,
|
||||
);
|
||||
}
|
||||
|
||||
assertFilesExist(files);
|
||||
|
||||
if (probeOnly) {
|
||||
process.stdout.write(`${JSON.stringify({ extension, files, ok: true }, null, 2)}\n`);
|
||||
return;
|
||||
}
|
||||
|
||||
process.exit(runFastChannelSmoke(extension));
|
||||
}
|
||||
|
||||
const entryHref = process.argv[1] ? pathToFileURL(path.resolve(process.argv[1])).href : "";
|
||||
|
||||
if (import.meta.url === entryHref) {
|
||||
main();
|
||||
}
|
||||
183
scripts/e2e/multipass-openclaw-bridge.mjs
Normal file
183
scripts/e2e/multipass-openclaw-bridge.mjs
Normal file
@ -0,0 +1,183 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath, pathToFileURL } from "node:url";
|
||||
import { resolveFastChannelTestFiles } from "./extension-fast-channel-smoke.mjs";
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
const repoRoot = path.resolve(__dirname, "..", "..");
|
||||
|
||||
function readStdin() {
|
||||
return new Promise((resolve, reject) => {
|
||||
let raw = "";
|
||||
process.stdin.setEncoding("utf8");
|
||||
process.stdin.on("data", (chunk) => {
|
||||
raw += chunk;
|
||||
});
|
||||
process.stdin.on("end", () => {
|
||||
resolve(raw);
|
||||
});
|
||||
process.stdin.on("error", reject);
|
||||
});
|
||||
}
|
||||
|
||||
function parseInput(raw) {
|
||||
if (!raw.trim()) {
|
||||
return {};
|
||||
}
|
||||
return JSON.parse(raw);
|
||||
}
|
||||
|
||||
function safeFileName(value) {
|
||||
return value.replace(/[^a-zA-Z0-9_.-]/g, "_");
|
||||
}
|
||||
|
||||
function stateFilePath(stateDir, providerId) {
|
||||
mkdirSync(stateDir, { recursive: true });
|
||||
return path.join(stateDir, `${safeFileName(providerId)}.json`);
|
||||
}
|
||||
|
||||
function writeJson(filePath, value) {
|
||||
writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`, "utf8");
|
||||
}
|
||||
|
||||
function readJson(filePath) {
|
||||
try {
|
||||
return JSON.parse(readFileSync(filePath, "utf8"));
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function runFastSmoke(extension) {
|
||||
if (!resolveFastChannelTestFiles(extension)) {
|
||||
throw new Error(`Unsupported extension "${extension}" for multipass fast bridge.`);
|
||||
}
|
||||
const result = spawnSync(
|
||||
"node",
|
||||
["scripts/e2e/extension-fast-channel-smoke.mjs", "--extension", extension],
|
||||
{
|
||||
cwd: repoRoot,
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
shell: process.platform === "win32",
|
||||
encoding: "utf8",
|
||||
env: process.env,
|
||||
},
|
||||
);
|
||||
if (result.status !== 0) {
|
||||
if (result.stdout) {
|
||||
process.stderr.write(result.stdout);
|
||||
}
|
||||
if (result.stderr) {
|
||||
process.stderr.write(result.stderr);
|
||||
}
|
||||
throw new Error(
|
||||
`Fast smoke failed for extension "${extension}" (exit ${String(result.status)}).`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function resolveThreadId(payload, fallbackId) {
|
||||
const target = payload?.outbound?.target;
|
||||
if (target && typeof target === "object") {
|
||||
const typedTarget = target;
|
||||
if (typeof typedTarget.threadId === "string" && typedTarget.threadId.trim()) {
|
||||
return typedTarget.threadId.trim();
|
||||
}
|
||||
if (typeof typedTarget.channelId === "string" && typedTarget.channelId.trim()) {
|
||||
return typedTarget.channelId.trim();
|
||||
}
|
||||
if (typeof typedTarget.id === "string" && typedTarget.id.trim()) {
|
||||
return typedTarget.id.trim();
|
||||
}
|
||||
}
|
||||
return fallbackId;
|
||||
}
|
||||
|
||||
function renderUsage() {
|
||||
process.stderr.write(
|
||||
"Usage: node scripts/e2e/multipass-openclaw-bridge.mjs <probe|send|wait> <extension> <state-dir>\n",
|
||||
);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const mode = process.argv[2] ?? "";
|
||||
const extension = process.argv[3] ?? "";
|
||||
const stateDir = process.argv[4] ?? "";
|
||||
if (!mode || !extension || !stateDir) {
|
||||
renderUsage();
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const rawInput = await readStdin();
|
||||
const payload = parseInput(rawInput);
|
||||
const providerId =
|
||||
(typeof payload?.provider?.id === "string" && payload.provider.id) ||
|
||||
`${extension}-multipass-fast`;
|
||||
const statePath = stateFilePath(stateDir, providerId);
|
||||
|
||||
if (mode === "probe") {
|
||||
process.stdout.write(
|
||||
`${JSON.stringify(
|
||||
{
|
||||
healthy: true,
|
||||
details: [`extension=${extension}`, `repo=${repoRoot}`, `state=${statePath}`],
|
||||
},
|
||||
null,
|
||||
2,
|
||||
)}\n`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mode === "send") {
|
||||
runFastSmoke(extension);
|
||||
const threadId = resolveThreadId(payload, `${extension}:ci-fast-target`);
|
||||
const messageId = `${extension}-fast-${Date.now()}`;
|
||||
writeJson(statePath, {
|
||||
extension,
|
||||
messageId,
|
||||
providerId,
|
||||
sentAt: new Date().toISOString(),
|
||||
text: typeof payload?.outbound?.text === "string" ? payload.outbound.text : "",
|
||||
threadId,
|
||||
});
|
||||
process.stdout.write(`${JSON.stringify({ accepted: true, messageId, threadId }, null, 2)}\n`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mode === "wait") {
|
||||
const currentState = readJson(statePath) ?? {};
|
||||
const waitNonce = typeof payload?.wait?.nonce === "string" ? payload.wait.nonce.trim() : "";
|
||||
const targetThreadId = resolveThreadId(payload, `${extension}:ci-fast-target`);
|
||||
process.stdout.write(
|
||||
`${JSON.stringify(
|
||||
{
|
||||
message: {
|
||||
author: "assistant",
|
||||
id: `${extension}-inbound-${Date.now()}`,
|
||||
sentAt: new Date().toISOString(),
|
||||
text: `ACK ${waitNonce || currentState.text || "nonce-missing"}`,
|
||||
threadId:
|
||||
typeof currentState.threadId === "string" && currentState.threadId.trim()
|
||||
? currentState.threadId
|
||||
: targetThreadId,
|
||||
},
|
||||
},
|
||||
null,
|
||||
2,
|
||||
)}\n`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
throw new Error(`Unknown mode "${mode}". Expected probe, send, or wait.`);
|
||||
}
|
||||
|
||||
const entryHref = process.argv[1] ? pathToFileURL(path.resolve(process.argv[1])).href : "";
|
||||
if (import.meta.url === entryHref) {
|
||||
await main();
|
||||
}
|
||||
151
scripts/e2e/run-multipass-extension-fast.mjs
Normal file
151
scripts/e2e/run-multipass-extension-fast.mjs
Normal file
@ -0,0 +1,151 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { mkdtempSync, mkdirSync, writeFileSync } from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath, pathToFileURL } from "node:url";
|
||||
import {
|
||||
listFastChannelExtensions,
|
||||
resolveFastChannelTestFiles,
|
||||
} from "./extension-fast-channel-smoke.mjs";
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
const repoRoot = path.resolve(__dirname, "..", "..");
|
||||
|
||||
function parseArgs(argv) {
|
||||
let extension = "";
|
||||
let multipassDir = "";
|
||||
for (let index = 0; index < argv.length; index += 1) {
|
||||
const arg = argv[index];
|
||||
if (arg === "--extension") {
|
||||
extension = argv[index + 1] ?? "";
|
||||
index += 1;
|
||||
continue;
|
||||
}
|
||||
if (arg === "--multipass-dir") {
|
||||
multipassDir = argv[index + 1] ?? "";
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
return { extension, multipassDir };
|
||||
}
|
||||
|
||||
function shellQuote(value) {
|
||||
return `'${String(value).replace(/'/g, "'\"'\"'")}'`;
|
||||
}
|
||||
|
||||
function usage(message) {
|
||||
if (message) {
|
||||
process.stderr.write(`${message}\n`);
|
||||
}
|
||||
process.stderr.write(
|
||||
"Usage: node scripts/e2e/run-multipass-extension-fast.mjs --extension <discord|imessage|line|signal|slack|telegram> --multipass-dir <path>\n",
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
function buildManifest(params) {
|
||||
const { extension, stateDir } = params;
|
||||
const fixtureId = `${extension}-openclaw-fast-roundtrip`;
|
||||
const bridgeScript = path.join(repoRoot, "scripts", "e2e", "multipass-openclaw-bridge.mjs");
|
||||
const providerId = `${extension}-openclaw-fast`;
|
||||
|
||||
const bridgeBase = `node ${shellQuote(bridgeScript)}`;
|
||||
const commands = {
|
||||
probe: `${bridgeBase} probe ${shellQuote(extension)} ${shellQuote(stateDir)}`,
|
||||
send: `${bridgeBase} send ${shellQuote(extension)} ${shellQuote(stateDir)}`,
|
||||
waitForInbound: `${bridgeBase} wait ${shellQuote(extension)} ${shellQuote(stateDir)}`,
|
||||
};
|
||||
|
||||
return {
|
||||
fixtureId,
|
||||
manifest: {
|
||||
configVersion: 1,
|
||||
userName: "openclaw-ci",
|
||||
providers: {
|
||||
[providerId]: {
|
||||
adapter: "script",
|
||||
platform: extension,
|
||||
capabilities: ["probe", "send", "roundtrip", "agent"],
|
||||
env: [],
|
||||
script: {
|
||||
commands,
|
||||
cwd: repoRoot,
|
||||
},
|
||||
status: "active",
|
||||
},
|
||||
},
|
||||
fixtures: [
|
||||
{
|
||||
id: fixtureId,
|
||||
provider: providerId,
|
||||
mode: "roundtrip",
|
||||
target: {
|
||||
id: `${extension}:ci-fast-target`,
|
||||
metadata: {},
|
||||
},
|
||||
inboundMatch: {
|
||||
author: "assistant",
|
||||
strategy: "contains",
|
||||
nonce: "contains",
|
||||
},
|
||||
timeoutMs: 20_000,
|
||||
retries: 0,
|
||||
tags: ["ci", "fast", extension],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function run() {
|
||||
const { extension, multipassDir } = parseArgs(process.argv.slice(2));
|
||||
if (!extension) {
|
||||
usage("Missing required --extension argument.");
|
||||
}
|
||||
if (!multipassDir) {
|
||||
usage("Missing required --multipass-dir argument.");
|
||||
}
|
||||
if (!resolveFastChannelTestFiles(extension)) {
|
||||
usage(
|
||||
`Unsupported extension "${extension}". Supported extensions: ${listFastChannelExtensions().join(", ")}`,
|
||||
);
|
||||
}
|
||||
|
||||
const scratchDir = mkdtempSync(path.join(os.tmpdir(), "openclaw-multipass-fast-"));
|
||||
const stateDir = path.join(scratchDir, "state");
|
||||
mkdirSync(stateDir, { recursive: true });
|
||||
const manifestPath = path.join(scratchDir, "multipass-fast.manifest.json");
|
||||
const { fixtureId, manifest } = buildManifest({ extension, stateDir });
|
||||
writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, "utf8");
|
||||
|
||||
const cmd = [
|
||||
"--dir",
|
||||
path.resolve(multipassDir),
|
||||
"dev",
|
||||
"roundtrip",
|
||||
fixtureId,
|
||||
"--config",
|
||||
manifestPath,
|
||||
"--json",
|
||||
];
|
||||
|
||||
const child = spawnSync("pnpm", cmd, {
|
||||
cwd: repoRoot,
|
||||
stdio: "inherit",
|
||||
shell: process.platform === "win32",
|
||||
env: process.env,
|
||||
});
|
||||
|
||||
if (typeof child.status === "number") {
|
||||
process.exit(child.status);
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const entryHref = process.argv[1] ? pathToFileURL(path.resolve(process.argv[1])).href : "";
|
||||
if (import.meta.url === entryHref) {
|
||||
run();
|
||||
}
|
||||
@ -10,6 +10,14 @@ const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
const repoRoot = path.resolve(__dirname, "..");
|
||||
const pnpm = "pnpm";
|
||||
export const MULTIPASS_FAST_EXTENSION_IDS = Object.freeze([
|
||||
"discord",
|
||||
"imessage",
|
||||
"line",
|
||||
"signal",
|
||||
"slack",
|
||||
"telegram",
|
||||
]);
|
||||
|
||||
function normalizeRelative(inputPath) {
|
||||
return inputPath.split(path.sep).join("/");
|
||||
@ -112,6 +120,13 @@ export function listChangedExtensionIds(params = {}) {
|
||||
return detectChangedExtensionIds(listChangedPaths(base, head));
|
||||
}
|
||||
|
||||
export function filterMultipassFastExtensionIds(extensionIds) {
|
||||
const allowed = new Set(MULTIPASS_FAST_EXTENSION_IDS);
|
||||
return extensionIds
|
||||
.filter((extensionId) => allowed.has(extensionId))
|
||||
.toSorted((left, right) => left.localeCompare(right));
|
||||
}
|
||||
|
||||
function resolveExtensionDirectory(targetArg, cwd = process.cwd()) {
|
||||
if (targetArg) {
|
||||
const asGiven = path.resolve(cwd, targetArg);
|
||||
|
||||
@ -3,7 +3,9 @@ import path from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
detectChangedExtensionIds,
|
||||
filterMultipassFastExtensionIds,
|
||||
listAvailableExtensionIds,
|
||||
MULTIPASS_FAST_EXTENSION_IDS,
|
||||
resolveExtensionTestPlan,
|
||||
} from "../../scripts/test-extension.mjs";
|
||||
|
||||
@ -72,4 +74,24 @@ describe("scripts/test-extension.mjs", () => {
|
||||
[...extensionIds].toSorted((left, right) => left.localeCompare(right)),
|
||||
);
|
||||
});
|
||||
|
||||
it("filters changed extensions to the multipass-fast matrix", () => {
|
||||
const filtered = filterMultipassFastExtensionIds([
|
||||
"line",
|
||||
"openrouter",
|
||||
"telegram",
|
||||
"microsoft",
|
||||
"discord",
|
||||
]);
|
||||
|
||||
expect(filtered).toEqual(["discord", "line", "telegram"]);
|
||||
expect(MULTIPASS_FAST_EXTENSION_IDS).toEqual([
|
||||
"discord",
|
||||
"imessage",
|
||||
"line",
|
||||
"signal",
|
||||
"slack",
|
||||
"telegram",
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user