Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
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
2 changes: 1 addition & 1 deletion packages/cli/test/cmd/store/analyze.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ describe("store analyze command", () => {
expect(version16).toHaveProperty("counts");
});

it("should show complete status for store with all files", async () => {
it.todo("should show complete status for store with all files", async () => {
const storePath = await testdir();

const singleFileTree = [{
Expand Down
6 changes: 5 additions & 1 deletion packages/fs-bridge/eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ export default luxass({
type: "lib",
pnpm: true,
}).append({
ignores: ["src/bridges/node.ts", ...GLOB_TESTS],
ignores: [
"src/bridges/node.ts",
"playgrounds/**",
...GLOB_TESTS,
],
rules: {
"no-restricted-imports": ["error", {
patterns: [
Expand Down
6 changes: 5 additions & 1 deletion packages/fs-bridge/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
".": "./dist/index.mjs",
"./bridges/http": "./dist/bridges/http.mjs",
"./bridges/node": "./dist/bridges/node.mjs",
"./errors": "./dist/errors.mjs",
"./package.json": "./package.json"
},
"main": "./dist/index.mjs",
Expand All @@ -42,7 +43,9 @@
"dev": "tsdown --watch",
"clean": "git clean -xdf dist node_modules",
"lint": "eslint .",
"typecheck": "tsc --noEmit"
"typecheck": "tsc --noEmit",
"playground:node": "tsx --tsconfig=./tsconfig.json ./playgrounds/node-playground.ts",
"playground:http": "tsx --tsconfig=./tsconfig.json ./playgrounds/http-playground.ts"
},
"dependencies": {
"@luxass/utils": "catalog:prod",
Expand All @@ -57,6 +60,7 @@
},
"devDependencies": {
"@luxass/eslint-config": "catalog:linting",
"@luxass/msw-utils": "catalog:prod",
"@ucdjs-internal/shared": "workspace:*",
"@ucdjs-tooling/tsconfig": "workspace:*",
"@ucdjs-tooling/tsdown-config": "workspace:*",
Expand Down
262 changes: 262 additions & 0 deletions packages/fs-bridge/playgrounds/http-playground.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
/* eslint-disable no-console, antfu/no-top-level-await */
/**
* fs-bridge HTTP Playground
*
* This playground verifies the HTTP fs-bridge works correctly against
* the real UCD.js API. It tests read, exists, and listdir operations.
*
* Configure with: FS_BRIDGE_HTTP_BASE_URL env var
* Run with: pnpm playground:http
*/

import process from "node:process";
import { assertCapability } from "../src";
import HTTPFileSystemBridge from "../src/bridges/http";

interface TestCase {
description: string;
run: () => Promise<void>;
}

const BASE_URL = process.env.FS_BRIDGE_HTTP_BASE_URL || "https://api.ucdjs.dev/api/v1/files";

console.log("fs-bridge HTTP Playground\n");
console.log("=".repeat(60));
console.log(`\nBase URL: ${BASE_URL}\n`);

const bridge = HTTPFileSystemBridge({ baseUrl: BASE_URL });

console.log("=".repeat(60));

const testCases: TestCase[] = [
// Capability tests (HTTP bridge is read-only)
{
description: "Bridge does NOT have write capability",
async run() {
if (bridge.optionalCapabilities.write) throw new Error("Should not have write");
try {
assertCapability(bridge, "write");
throw new Error("Should have thrown");
} catch (err) {
if (err instanceof Error && err.message === "Should have thrown") throw err;
}
},
},
{
description: "Bridge does NOT have mkdir capability",
async run() {
if (bridge.optionalCapabilities.mkdir) throw new Error("Should not have mkdir");
try {
assertCapability(bridge, "mkdir");
throw new Error("Should have thrown");
} catch (err) {
if (err instanceof Error && err.message === "Should have thrown") throw err;
}
},
},
{
description: "Bridge does NOT have rm capability",
async run() {
if (bridge.optionalCapabilities.rm) throw new Error("Should not have rm");
try {
assertCapability(bridge, "rm");
throw new Error("Should have thrown");
} catch (err) {
if (err instanceof Error && err.message === "Should have thrown") throw err;
}
},
},

// Read tests
{
description: "Read 16.0.0/ReadMe.txt",
async run() {
const content = await bridge.read("16.0.0/ReadMe.txt");
if (typeof content !== "string") throw new Error("Content should be string");
if (content.length === 0) throw new Error("Content should not be empty");
if (!content.includes("Unicode")) throw new Error("Should mention Unicode");
},
},
{
description: "Read with / prefix",
async run() {
const content = await bridge.read("/16.0.0/ReadMe.txt");
if (!content.includes("Unicode")) throw new Error("Should work with / prefix");
},
},
{
description: "Read non-existent file throws",
async run() {
try {
await bridge.read("16.0.0/NonExistent12345.txt");
throw new Error("Should have thrown");
} catch (err) {
if (err instanceof Error && err.message === "Should have thrown") throw err;
}
},
},
{
description: "Read trailing slash throws",
async run() {
try {
await bridge.read("16.0.0/ReadMe.txt/");
throw new Error("Should have thrown");
} catch (err) {
if (err instanceof Error && err.message === "Should have thrown") throw err;
}
},
},

// Exists tests
{
description: "Exists returns true for 16.0.0/ReadMe.txt",
async run() {
if (!(await bridge.exists("16.0.0/ReadMe.txt"))) throw new Error("Should exist");
},
},
{
description: "Exists returns false for non-existent",
async run() {
if (await bridge.exists("16.0.0/NonExistent12345.txt")) throw new Error("Should not exist");
},
},
{
description: "Exists with / prefix",
async run() {
if (!(await bridge.exists("/16.0.0/ReadMe.txt"))) throw new Error("Should exist");
},
},

// Listdir tests
{
description: "Listdir 16.0.0 returns entries",
async run() {
const entries = await bridge.listdir("16.0.0");
if (!Array.isArray(entries)) throw new Error("Should return array");
if (entries.length === 0) throw new Error("Should have entries");
const hasReadme = entries.some((e) => e.name === "ReadMe.txt");
if (!hasReadme) throw new Error("Should contain ReadMe.txt");
},
},
{
description: "Listdir shallow has empty children",
async run() {
const entries = await bridge.listdir("16.0.0");
const dir = entries.find((e) => e.type === "directory");
if (dir && dir.type === "directory" && dir.children.length > 0) {
throw new Error("Shallow should have empty children");
}
},
},
{
description: "Listdir recursive populates children",
async run() {
const entries = await bridge.listdir("16.0.0/ucd", true);
// Note: this may not find a dir with children if ucd has no subdirs
if (!Array.isArray(entries)) throw new Error("Should return array");
},
},
{
description: "Listdir non-existent returns empty array",
async run() {
const entries = await bridge.listdir("NonExistentVersion12345");
if (!Array.isArray(entries) || entries.length !== 0) {
throw new Error("Should return empty array");
}
},
},
{
description: "Listdir with / prefix",
async run() {
const entries = await bridge.listdir("/16.0.0");
if (entries.length === 0) throw new Error("Should have entries");
},
},

// Unsupported operations throw
{
description: "Write operation throws",
async run() {
try {
await bridge.write?.("test.txt", "content");
throw new Error("Should have thrown");
} catch (err) {
if (err instanceof Error && err.message === "Should have thrown") throw err;
}
},
},
{
description: "Mkdir operation throws",
async run() {
try {
await bridge.mkdir?.("new-dir");
throw new Error("Should have thrown");
} catch (err) {
if (err instanceof Error && err.message === "Should have thrown") throw err;
}
},
},
{
description: "Rm operation throws",
async run() {
try {
await bridge.rm?.("test.txt");
throw new Error("Should have thrown");
} catch (err) {
if (err instanceof Error && err.message === "Should have thrown") throw err;
}
},
},

// Bridge metadata
{
description: "Bridge has correct metadata",
async run() {
if (bridge.meta.name !== "HTTP File System Bridge") throw new Error("Wrong name");
if (typeof bridge.meta.description !== "string") throw new Error("Missing description");
},
},

// Complex workflow
{
description: "Discover and read UnicodeData.txt",
async run() {
const entries = await bridge.listdir("16.0.0/ucd");
const unicodeData = entries.find((e) => e.name === "UnicodeData.txt");
if (!unicodeData) throw new Error("UnicodeData.txt not found");
if (unicodeData.type !== "file") throw new Error("Should be a file");

const exists = await bridge.exists("16.0.0/ucd/UnicodeData.txt");
if (!exists) throw new Error("Should exist");

const content = await bridge.read("16.0.0/ucd/UnicodeData.txt");
if (!content.includes(";")) throw new Error("Should contain semicolons");
},
},
];

console.log("\nRunning test cases:\n");

let passed = 0;
let failed = 0;

for (const testCase of testCases) {
console.log(` ${testCase.description}... `);

try {
await testCase.run();
console.log("PASS");
passed++;
} catch (error) {
console.log("FAIL");
console.log(` Error: ${(error as Error).message}`);
failed++;
}
}

console.log(`\n${"=".repeat(60)}`);
console.log(`\nResults: ${passed} passed, ${failed} failed\n`);

if (failed > 0) {
process.exit(1);
}
Loading
Loading