repositories / pi-ext
pi-ext
bugabingas pi extensions
owned by admin
extensions/os-notifier/__serial__/package.test.ts
Rawimport {
chmodSync,
cpSync,
mkdirSync,
mkdtempSync,
readdirSync,
readFileSync,
rmSync,
statSync,
writeFileSync,
} from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { fileURLToPath, pathToFileURL } from "node:url";
import { describe, expect, it } from "vitest";
import {
createPiSession,
type PiSession,
} from "../../../test/fixture/create-pi-session.ts";
const built = fileURLToPath(
new URL("../../../dist/os-notifier", import.meta.url),
);
describe.skipIf(
process.env["PI_EXT_VERIFY_OS_NOTIFIER_PACKAGE"] !== "1" ||
process.platform === "win32",
)("os-notifier relocated focus resource", () => {
it("loads the JAR only on demand and makes an extracted writable copy safe for Android", async () => {
const root = mkdtempSync(join(tmpdir(), "os-notifier-package-"));
const target = join(root, "package");
let session: PiSession | undefined;
try {
cpSync(built, target, { recursive: true });
const pkg = JSON.parse(
readFileSync(join(target, "package.json"), "utf8"),
) as {
pi: { extensions: string[] };
};
const entry = pkg.pi.extensions[0];
if (!entry) throw new Error("os-notifier Pi extension entry missing");
const jar = join(target, "android/focus.jar");
expect(readdirSync(join(target, "android"))).toEqual(["focus.jar"]);
expect(readFileSync(jar)).toEqual(
readFileSync(
fileURLToPath(new URL("../android/focus.jar", import.meta.url)),
),
);
chmodSync(jar, 0o644);
const bin = join(root, "bin");
mkdirSync(bin);
const queryLog = join(root, "query.log");
const notifyLog = join(root, "notification.log");
writeFileSync(
join(bin, "app_process"),
`#!${process.execPath}\nrequire("node:fs").writeFileSync(process.env.TEST_FOCUS_LOG, process.env.CLASSPATH);process.exit(0);\n`,
);
writeFileSync(
join(bin, "termux-notification"),
`#!${process.execPath}\nrequire("node:fs").writeFileSync(process.env.TEST_NOTIFY_LOG, "posted");\n`,
);
writeFileSync(
join(bin, "termux-vibrate"),
`#!${process.execPath}\nprocess.exit(0);\n`,
);
for (const name of readdirSync(bin)) chmodSync(join(bin, name), 0o755);
session = await createPiSession({
cwd: root,
extensionPaths: [join(target, entry)],
env: {
TERMUX_VERSION: "test",
PATH: bin,
TEST_FOCUS_LOG: queryLog,
TEST_NOTIFY_LOG: notifyLog,
},
});
expect(session.errors).toEqual([]);
session.session.extensionRunner.setUIContext(
session.session.extensionRunner.getUIContext(),
"tui",
);
await session.session.extensionRunner.emit({
type: "session_start",
reason: "startup",
});
expect(statSync(jar).mode & 0o222).toBe(0o200);
expect(() => readFileSync(queryLog)).toThrow();
await session.session.extensionRunner.emit({
type: "input",
text: "test",
source: "interactive",
} as never);
await session.session.extensionRunner.emitBeforeAgentStart(
{ messageType: "user", prompt: "test" },
{ cwd: root },
);
await session.session.extensionRunner.emit({
type: "agent_start",
} as never);
await session.session.extensionRunner.emit({
type: "agent_end",
messages: [{ role: "assistant", stopReason: "stop" }],
} as never);
await session.session.extensionRunner.emit({
type: "agent_settled",
} as never);
expect(readFileSync(queryLog, "utf8")).toBe(jar);
expect(statSync(jar).mode & 0o222).toBe(0);
await expect
.poll(() => {
try {
return readFileSync(notifyLog, "utf8");
} catch {
return "";
}
})
.toBe("posted");
await session.dispose();
session = undefined;
if (process.platform === "android") {
// Independent native check: the actual Android runtime loads relocated DEX.
const chunk = readdirSync(target).find((name) =>
/^termux-focus-.*\.js$/.test(name),
);
if (!chunk) throw new Error("packaged focus chunk missing");
const helper = (await import(
pathToFileURL(join(target, chunk)).href
)) as { termuxFocused: () => Promise<boolean | undefined> };
expect([true, false]).toContain(await helper.termuxFocused());
}
} finally {
await session?.dispose();
rmSync(root, { recursive: true, force: true });
}
});
});