90 lines
3.3 KiB
JavaScript
90 lines
3.3 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import { existsSync, lstatSync, mkdirSync, mkdtempSync, readlinkSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
import { after, before, test } from "node:test";
|
|
|
|
import { linkSdkInto, readPluginsUnder } from "./link-plugin-dev-sdk.mjs";
|
|
|
|
let workDir;
|
|
|
|
before(() => {
|
|
workDir = mkdtempSync(join(tmpdir(), "link-plugin-dev-sdk-"));
|
|
});
|
|
|
|
after(() => {
|
|
rmSync(workDir, { force: true, recursive: true });
|
|
});
|
|
|
|
function makePackage(dir) {
|
|
mkdirSync(dir, { recursive: true });
|
|
writeFileSync(join(dir, "package.json"), "{}\n");
|
|
return dir;
|
|
}
|
|
|
|
test("readPluginsUnder returns [] for a missing directory", () => {
|
|
assert.deepEqual(readPluginsUnder(join(workDir, "does-not-exist")), []);
|
|
});
|
|
|
|
test("readPluginsUnder finds first-level package directories", () => {
|
|
const parent = join(workDir, "first-level");
|
|
const a = makePackage(join(parent, "a"));
|
|
const b = makePackage(join(parent, "b"));
|
|
|
|
assert.deepEqual(readPluginsUnder(parent).sort(), [a, b].sort());
|
|
});
|
|
|
|
test("readPluginsUnder recurses into directories that are not themselves packages", () => {
|
|
// Mirrors the recursive pnpm-workspace exclusion glob: a provider nested
|
|
// deeper than one level must still be discovered.
|
|
const parent = join(workDir, "nested");
|
|
const nested = makePackage(join(parent, "vendor", "my-plugin"));
|
|
|
|
assert.deepEqual(readPluginsUnder(parent), [nested]);
|
|
});
|
|
|
|
test("readPluginsUnder stops descending once a package.json is found and skips node_modules", () => {
|
|
const parent = join(workDir, "boundaries");
|
|
const pkg = makePackage(join(parent, "plugin"));
|
|
// A nested package inside an already-matched package must not be returned.
|
|
makePackage(join(pkg, "sub-package"));
|
|
// node_modules must be ignored entirely.
|
|
makePackage(join(parent, "node_modules", "some-dep"));
|
|
|
|
assert.deepEqual(readPluginsUnder(parent), [pkg]);
|
|
});
|
|
|
|
test("linkSdkInto creates the plugin-sdk symlink and is idempotent", () => {
|
|
const pkg = makePackage(join(workDir, "link-target"));
|
|
|
|
assert.equal(linkSdkInto(pkg), true);
|
|
|
|
const link = join(pkg, "node_modules", "@paperclipai", "plugin-sdk");
|
|
assert.ok(lstatSync(link).isSymbolicLink());
|
|
|
|
// Second call is a no-op because the link already points at the in-repo SDK.
|
|
assert.equal(linkSdkInto(pkg), false);
|
|
});
|
|
|
|
test("linkSdkInto leaves a real (non-symlink) install in place", () => {
|
|
const pkg = makePackage(join(workDir, "real-install"));
|
|
const scopeDir = join(pkg, "node_modules", "@paperclipai");
|
|
mkdirSync(scopeDir, { recursive: true });
|
|
// Simulate a published-tarball install: a real directory, not a symlink.
|
|
makePackage(join(scopeDir, "plugin-sdk"));
|
|
|
|
assert.equal(linkSdkInto(pkg), false);
|
|
assert.ok(!lstatSync(join(scopeDir, "plugin-sdk")).isSymbolicLink());
|
|
});
|
|
|
|
test("linkSdkInto replaces a symlink that points somewhere else", () => {
|
|
const pkg = makePackage(join(workDir, "stale-link"));
|
|
const scopeDir = join(pkg, "node_modules", "@paperclipai");
|
|
mkdirSync(scopeDir, { recursive: true });
|
|
symlinkSync("../somewhere-else", join(scopeDir, "plugin-sdk"), "dir");
|
|
|
|
assert.equal(linkSdkInto(pkg), true);
|
|
assert.notEqual(readlinkSync(join(scopeDir, "plugin-sdk")), "../somewhere-else");
|
|
assert.ok(existsSync(scopeDir));
|
|
});
|