refactor: centralize test path containment checks
This commit is contained in:
@@ -4,27 +4,34 @@ import JSZip from "jszip";
|
|||||||
import sharp from "sharp";
|
import sharp from "sharp";
|
||||||
import { describe, expect, it, vi } from "vitest";
|
import { describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
|
import { isPathWithinBase } from "../../test/helpers/paths.js";
|
||||||
import { withTempHome } from "../../test/helpers/temp-home.js";
|
import { withTempHome } from "../../test/helpers/temp-home.js";
|
||||||
|
|
||||||
describe("media store", () => {
|
describe("media store", () => {
|
||||||
it("creates and returns media directory", async () => {
|
async function withTempStore<T>(
|
||||||
await withTempHome(async () => {
|
fn: (store: typeof import("./store.js"), home: string) => Promise<T>,
|
||||||
|
): Promise<T> {
|
||||||
|
return await withTempHome(async (home) => {
|
||||||
vi.resetModules();
|
vi.resetModules();
|
||||||
const store = await import("./store.js");
|
const store = await import("./store.js");
|
||||||
|
return await fn(store, home);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
it("creates and returns media directory", async () => {
|
||||||
|
await withTempStore(async (store, home) => {
|
||||||
const dir = await store.ensureMediaDir();
|
const dir = await store.ensureMediaDir();
|
||||||
const normalized = path.normalize(dir);
|
expect(isPathWithinBase(home, dir)).toBe(true);
|
||||||
expect(normalized).toContain(`${path.sep}.clawdbot${path.sep}media`);
|
expect(path.normalize(dir)).toContain(
|
||||||
|
`${path.sep}.clawdbot${path.sep}media`,
|
||||||
|
);
|
||||||
const stat = await fs.stat(dir);
|
const stat = await fs.stat(dir);
|
||||||
expect(stat.isDirectory()).toBe(true);
|
expect(stat.isDirectory()).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("saves buffers and enforces size limit", async () => {
|
it("saves buffers and enforces size limit", async () => {
|
||||||
await withTempHome(async () => {
|
await withTempStore(async (store) => {
|
||||||
vi.resetModules();
|
|
||||||
const store = await import("./store.js");
|
|
||||||
|
|
||||||
const buf = Buffer.from("hello");
|
const buf = Buffer.from("hello");
|
||||||
const saved = await store.saveMediaBuffer(buf, "text/plain");
|
const saved = await store.saveMediaBuffer(buf, "text/plain");
|
||||||
const savedStat = await fs.stat(saved.path);
|
const savedStat = await fs.stat(saved.path);
|
||||||
@@ -49,10 +56,7 @@ describe("media store", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("copies local files and cleans old media", async () => {
|
it("copies local files and cleans old media", async () => {
|
||||||
await withTempHome(async (home) => {
|
await withTempStore(async (store, home) => {
|
||||||
vi.resetModules();
|
|
||||||
const store = await import("./store.js");
|
|
||||||
|
|
||||||
const srcFile = path.join(home, "tmp-src.txt");
|
const srcFile = path.join(home, "tmp-src.txt");
|
||||||
await fs.mkdir(home, { recursive: true });
|
await fs.mkdir(home, { recursive: true });
|
||||||
await fs.writeFile(srcFile, "local file");
|
await fs.writeFile(srcFile, "local file");
|
||||||
@@ -71,10 +75,7 @@ describe("media store", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("sets correct mime for xlsx by extension", async () => {
|
it("sets correct mime for xlsx by extension", async () => {
|
||||||
await withTempHome(async (home) => {
|
await withTempStore(async (store, home) => {
|
||||||
vi.resetModules();
|
|
||||||
const store = await import("./store.js");
|
|
||||||
|
|
||||||
const xlsxPath = path.join(home, "sheet.xlsx");
|
const xlsxPath = path.join(home, "sheet.xlsx");
|
||||||
await fs.mkdir(home, { recursive: true });
|
await fs.mkdir(home, { recursive: true });
|
||||||
await fs.writeFile(xlsxPath, "not really an xlsx");
|
await fs.writeFile(xlsxPath, "not really an xlsx");
|
||||||
@@ -88,10 +89,7 @@ describe("media store", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("renames media based on detected mime even when extension is wrong", async () => {
|
it("renames media based on detected mime even when extension is wrong", async () => {
|
||||||
await withTempHome(async (home) => {
|
await withTempStore(async (store, home) => {
|
||||||
vi.resetModules();
|
|
||||||
const store = await import("./store.js");
|
|
||||||
|
|
||||||
const pngBytes = await sharp({
|
const pngBytes = await sharp({
|
||||||
create: { width: 2, height: 2, channels: 3, background: "#00ff00" },
|
create: { width: 2, height: 2, channels: 3, background: "#00ff00" },
|
||||||
})
|
})
|
||||||
@@ -110,10 +108,7 @@ describe("media store", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("sniffs xlsx mime for zip buffers and renames extension", async () => {
|
it("sniffs xlsx mime for zip buffers and renames extension", async () => {
|
||||||
await withTempHome(async (home) => {
|
await withTempStore(async (store, home) => {
|
||||||
vi.resetModules();
|
|
||||||
const store = await import("./store.js");
|
|
||||||
|
|
||||||
const zip = new JSZip();
|
const zip = new JSZip();
|
||||||
zip.file(
|
zip.file(
|
||||||
"[Content_Types].xml",
|
"[Content_Types].xml",
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import path from "node:path";
|
|||||||
|
|
||||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
|
import { isPathWithinBase } from "../../test/helpers/paths.js";
|
||||||
import { withTempHome } from "../../test/helpers/temp-home.js";
|
import { withTempHome } from "../../test/helpers/temp-home.js";
|
||||||
|
|
||||||
const runtime = {
|
const runtime = {
|
||||||
@@ -28,26 +29,7 @@ describe("web logout", () => {
|
|||||||
vi.resetModules();
|
vi.resetModules();
|
||||||
const { logoutWeb, WA_WEB_AUTH_DIR } = await import("./session.js");
|
const { logoutWeb, WA_WEB_AUTH_DIR } = await import("./session.js");
|
||||||
|
|
||||||
if (process.platform === "win32") {
|
expect(isPathWithinBase(home, WA_WEB_AUTH_DIR)).toBe(true);
|
||||||
const normalizedHome = path.win32.normalize(home).toLowerCase();
|
|
||||||
const normalizedAuthDir = path.win32
|
|
||||||
.normalize(WA_WEB_AUTH_DIR)
|
|
||||||
.toLowerCase();
|
|
||||||
const rel = path.win32.relative(normalizedHome, normalizedAuthDir);
|
|
||||||
const isWithinHome =
|
|
||||||
rel.length > 0 &&
|
|
||||||
!rel.startsWith("..") &&
|
|
||||||
!path.win32.isAbsolute(rel);
|
|
||||||
expect(isWithinHome).toBe(true);
|
|
||||||
} else {
|
|
||||||
const rel = path.relative(
|
|
||||||
path.resolve(home),
|
|
||||||
path.resolve(WA_WEB_AUTH_DIR),
|
|
||||||
);
|
|
||||||
expect(rel && !rel.startsWith("..") && !path.isAbsolute(rel)).toBe(
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fs.mkdirSync(WA_WEB_AUTH_DIR, { recursive: true });
|
fs.mkdirSync(WA_WEB_AUTH_DIR, { recursive: true });
|
||||||
fs.writeFileSync(path.join(WA_WEB_AUTH_DIR, "creds.json"), "{}");
|
fs.writeFileSync(path.join(WA_WEB_AUTH_DIR, "creds.json"), "{}");
|
||||||
|
|||||||
19
test/helpers/paths.ts
Normal file
19
test/helpers/paths.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import path from "node:path";
|
||||||
|
|
||||||
|
export function isPathWithinBase(base: string, target: string): boolean {
|
||||||
|
if (process.platform === "win32") {
|
||||||
|
const normalizedBase = path.win32.normalize(path.win32.resolve(base));
|
||||||
|
const normalizedTarget = path.win32.normalize(path.win32.resolve(target));
|
||||||
|
|
||||||
|
const rel = path.win32.relative(
|
||||||
|
normalizedBase.toLowerCase(),
|
||||||
|
normalizedTarget.toLowerCase(),
|
||||||
|
);
|
||||||
|
return rel === "" || (!rel.startsWith("..") && !path.win32.isAbsolute(rel));
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizedBase = path.resolve(base);
|
||||||
|
const normalizedTarget = path.resolve(target);
|
||||||
|
const rel = path.relative(normalizedBase, normalizedTarget);
|
||||||
|
return rel === "" || (!rel.startsWith("..") && !path.isAbsolute(rel));
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user