Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 148 additions & 0 deletions frontend/src/components/storage/__tests__/storage-inspector.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
/* Copyright 2026 Marimo. All rights reserved. */
import { describe, expect, it } from "vitest";
import type { StorageEntry, StoragePathKey } from "@/core/storage/types";
import { storagePathKey } from "@/core/storage/types";
import { exportedForTesting } from "../storage-inspector";

const { filterEntries, remoteSearchPrefix } = exportedForTesting;

function makeEntry(
overrides: Partial<StorageEntry> & { path: string },
): StorageEntry {
return {
kind: overrides.kind ?? "file",
lastModified: overrides.lastModified ?? null,
metadata: overrides.metadata ?? {},
path: overrides.path,
size: overrides.size ?? 0,
};
}

describe("storage inspector search", () => {
describe("remoteSearchPrefix", () => {
it("returns the parent directory of the query so backends can list it", () => {
// Object stores match prefixes on path segments, so we list the parent
// directory and filter the rest client-side.
expect(remoteSearchPrefix("folder/x")).toBe("folder/");
expect(remoteSearchPrefix("nested/folder/x")).toBe("nested/folder/");
});

it("preserves trailing slashes when the user explicitly typed one", () => {
expect(remoteSearchPrefix("folder/x/")).toBe("folder/x/");
expect(remoteSearchPrefix("nested/folder/x/")).toBe("nested/folder/x/");
expect(remoteSearchPrefix("/")).toBe("/");
});

it("returns an empty string for queries without a directory component", () => {
// Without a slash there is nothing useful to send to the backend; rely on
// local filtering instead.
expect(remoteSearchPrefix("x")).toBe("");
expect(remoteSearchPrefix("report")).toBe("");
});

it("trims leading/trailing whitespace before computing the prefix", () => {
expect(remoteSearchPrefix(" nested/folder/x ")).toBe("nested/folder/");
expect(remoteSearchPrefix(" ")).toBe("");
});
});

describe("filterEntries", () => {
it("matches loaded entries by basename, extension, and full path", () => {
const entries = [
makeEntry({ path: "data/report.csv" }),
makeEntry({ path: "logs/readme.md" }),
];

expect(
filterEntries({
entries,
namespace: "store",
searchValue: ".csv",
entriesByPath: new Map(),
}),
).toEqual([entries[0]]);
expect(
filterEntries({
entries,
namespace: "store",
searchValue: "data/report",
entriesByPath: new Map(),
}),
).toEqual([entries[0]]);
expect(
filterEntries({
entries,
namespace: "store",
searchValue: "readme",
entriesByPath: new Map(),
}),
).toEqual([entries[1]]);
});

it("returns the original entries when search is cleared", () => {
const entries = [
makeEntry({ path: "data/report.csv" }),
makeEntry({ path: "logs/readme.md" }),
];

expect(
filterEntries({
entries,
namespace: "store",
searchValue: "",
entriesByPath: new Map(),
}),
).toBe(entries);
});

it("keeps directories whose loaded descendants match a path query", () => {
const directory = makeEntry({ path: "data", kind: "directory" });
const child = makeEntry({ path: "data/report.csv" });
const entriesByPath = new Map<StoragePathKey, StorageEntry[]>([
[storagePathKey("store", "data/"), [child]],
]);

expect(
filterEntries({
entries: [directory],
namespace: "store",
searchValue: "data/report",
entriesByPath,
}),
).toEqual([directory]);
});

it("matches partial path queries against loaded sibling files (folder/x -> folder/xsomething)", () => {
// Regression test for the obstore path-segment prefix bug: typing
// "folder/x" should surface "folder/xsomething" once the folder has
// been listed locally.
const folder = makeEntry({ path: "folder/", kind: "directory" });
const matching = makeEntry({ path: "folder/xsomething" });
const matchingAlt = makeEntry({ path: "folder/xanother" });
const other = makeEntry({ path: "folder/something" });
const entriesByPath = new Map<StoragePathKey, StorageEntry[]>([
[storagePathKey("store", "folder/"), [other, matchingAlt, matching]],
]);

expect(
filterEntries({
entries: [folder],
namespace: "store",
searchValue: "folder/x",
entriesByPath,
}),
).toEqual([folder]);

// Same query, but applied directly to the loaded folder contents (as
// happens for the "remote results" fallback) should drop non-matches.
expect(
filterEntries({
entries: [other, matchingAlt, matching],
namespace: "store",
searchValue: "folder/x",
entriesByPath,
}),
).toEqual([matchingAlt, matching]);
});
});
});
Loading
Loading