- engine/(registry/types/runtime/expression)物理迁入包, 解耦shared/types(域类型归包, 宿主type-only re-export) - 31个纯Node能力+27个测试迁入包; 宿主留守Electron/浏览器/AI依赖能力, index.ts经@rpa/core/capabilities拉起 - 新增createEngine()/defaultEngine: excel缓存/Excel句柄/db连接/FTP客户端/OCR实例/拟真设置挂引擎实例 - remaining-core子流程依赖改为host-bridge注入, 包内不再import宿主flows - 宿主main/index.ts启动时注入子流程源; electron-vite/vitest/tsconfig配@rpa/core源码别名 - 验证: typecheck绿, 快照909个id diff为零, 332测试过(2个桌面冒烟因会话无交互SetForegroundWindow被拒, 环境阻塞)
68 lines
2.8 KiB
TypeScript
68 lines
2.8 KiB
TypeScript
import net from "node:net";
|
|
import { afterEach, describe, expect, it } from "vitest";
|
|
import type { CatalogComponent, FlowNode } from "../types";
|
|
import type { RunContext } from "../engine/types";
|
|
import { getHandler, hasCapability } from "../engine/registry";
|
|
import "./message";
|
|
|
|
const context = {} as RunContext;
|
|
const component = {} as CatalogComponent;
|
|
const node = (id: string): FlowNode => ({ id, componentId: id, position: { x: 0, y: 0 }, values: {}, options: {} });
|
|
const ids = [
|
|
"Mail_FindMail", "Mail_FindMail_v1", "Mail_FindMail_v2",
|
|
"Mail_ReplyMail", "Mail_ReplyMail_v1", "Mail_ReplyMail_v2",
|
|
"Mail_DoGetMailMessage", "Mail_DoGetMailMessage_v1", "Mail_DoGetMailMessage_v2"
|
|
];
|
|
|
|
afterEach(async () => {});
|
|
|
|
describe("mail receive and reply", () => {
|
|
it("registers find, reply and Outlook message capabilities", () => {
|
|
for (const id of ids) expect(hasCapability(id), id).toBe(true);
|
|
});
|
|
|
|
it("replies through SMTP with the selected mail sender", async () => {
|
|
let received = "";
|
|
const server = net.createServer((socket) => {
|
|
socket.setEncoding("utf8");
|
|
socket.write("220 localhost ESMTP\r\n");
|
|
let buffer = "";
|
|
let dataMode = false;
|
|
socket.on("data", (chunk) => {
|
|
buffer += chunk;
|
|
const lines = buffer.split("\r\n");
|
|
buffer = lines.pop() || "";
|
|
for (const line of lines) {
|
|
if (dataMode) {
|
|
if (line === ".") {
|
|
dataMode = false;
|
|
socket.write("250 queued\r\n");
|
|
} else received += line + "\n";
|
|
} else if (/^EHLO/i.test(line)) socket.write("250-localhost\r\n250 OK\r\n");
|
|
else if (/^MAIL FROM/i.test(line)) socket.write("250 ok\r\n");
|
|
else if (/^RCPT TO/i.test(line)) socket.write("250 ok\r\n");
|
|
else if (/^DATA/i.test(line)) { dataMode = true; socket.write("354 end with dot\r\n"); }
|
|
else if (/^QUIT/i.test(line)) { socket.write("221 bye\r\n"); socket.end(); }
|
|
else socket.write("250 OK\r\n");
|
|
}
|
|
});
|
|
});
|
|
await new Promise<void>((resolve) => server.listen(0, "127.0.0.1", resolve));
|
|
const port = (server.address() as net.AddressInfo).port;
|
|
try {
|
|
const handler = getHandler("Mail_ReplyMail_v2");
|
|
expect(handler).toBeTruthy();
|
|
const result = await handler!(context, node("Mail_ReplyMail_v2"), {
|
|
account_obj: { host: "127.0.0.1", port, account: "robot@example.com", secure: "False" },
|
|
mail_obj: { from: "owner@example.com", subject: "status", messageId: "<abc@example.com>" },
|
|
content: "reply delivered"
|
|
}, component);
|
|
expect(result).toMatchObject({ accepted: ["owner@example.com"] });
|
|
expect(received).toContain("reply delivered");
|
|
expect(received).toContain("Re: status");
|
|
} finally {
|
|
await new Promise<void>((resolve) => server.close(() => resolve()));
|
|
}
|
|
}, 30000);
|
|
});
|