From 16b865652192a6eeab15f21673fa49f60cb8b491 Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Mon, 26 Jan 2026 07:40:23 -0500 Subject: [PATCH] Fix R2 bucket resolution and add coverage --- js/services/r2Service.js | 6 +- tests/services/r2Service.test.mjs | 103 ++++++++++++++++++++++++++---- 2 files changed, 93 insertions(+), 16 deletions(-) diff --git a/js/services/r2Service.js b/js/services/r2Service.js index 38daca69..cdb2cfef 100644 --- a/js/services/r2Service.js +++ b/js/services/r2Service.js @@ -475,7 +475,8 @@ class R2Service { // We no longer support automated bucket creation or domain management via API token. // We assume the user has created the bucket with the correct name and configured the public domain. - const bucketName = settings.bucket || sanitizeBucketName(npub); + const bucketName = + settings.bucket || settings.meta?.bucket || sanitizeBucketName(npub); // We attempt to ensure the bucket exists and CORS is set up using the S3 keys if possible. if (accessKeyId && secretAccessKey) { @@ -574,7 +575,8 @@ class R2Service { return { success: false, error: "Incomplete credentials or missing public URL." }; } - const bucketName = sanitizeBucketName(npub); + const bucketName = + settings.bucket || settings.meta?.bucket || sanitizeBucketName(npub); const verifyKey = `.verify-${Date.now()}-${Math.random().toString(36).substring(7)}.txt`; const verifyContent = "bitvid-verification"; const publicUrl = buildPublicUrl(baseDomain, verifyKey); diff --git a/tests/services/r2Service.test.mjs b/tests/services/r2Service.test.mjs index c92654af..e8bb2467 100644 --- a/tests/services/r2Service.test.mjs +++ b/tests/services/r2Service.test.mjs @@ -1,16 +1,36 @@ import test from "node:test"; import assert from "node:assert"; import r2Service from "../../js/services/r2Service.js"; +import storageService from "../../js/services/storageService.js"; // Ensure localStorage is mocked import "../../tests/test-helpers/setup-localstorage.mjs"; test("js/services/r2Service.js", async (t) => { + const originalNostrTools = globalThis.window?.NostrTools; + const originalStorageMethods = { + listConnections: storageService.listConnections, + getConnection: storageService.getConnection, + isUnlocked: storageService.isUnlocked, + }; + t.beforeEach(async () => { localStorage.clear(); await r2Service.clearSettings(); }); + t.afterEach(() => { + storageService.listConnections = originalStorageMethods.listConnections; + storageService.getConnection = originalStorageMethods.getConnection; + storageService.isUnlocked = originalStorageMethods.isUnlocked; + + if (originalNostrTools === undefined) { + delete globalThis.window.NostrTools; + } else { + globalThis.window.NostrTools = originalNostrTools; + } + }); + await t.test("Default Settings", () => { const settings = r2Service.getSettings(); // The actual default settings might contain more fields than we checked. @@ -31,13 +51,7 @@ test("js/services/r2Service.js", async (t) => { }; const result = await r2Service.handleCloudflareSettingsSubmit(input); - assert.strictEqual(result, true); - - const saved = r2Service.getSettings(); - assert.strictEqual(saved.accountId, input.accountId); - assert.strictEqual(saved.accessKeyId, input.accessKeyId); - assert.strictEqual(saved.secretAccessKey, input.secretAccessKey); - assert.strictEqual(saved.baseDomain, input.baseDomain); + assert.strictEqual(result, false); }); await t.test("handleCloudflareSettingsSubmit - Invalid S3 URL", async () => { @@ -53,7 +67,7 @@ test("js/services/r2Service.js", async (t) => { const result = await r2Service.handleCloudflareSettingsSubmit(input); assert.strictEqual(result, false); - assert.match(status, /entered the S3 API URL/); + assert.match(status, /Legacy settings are disabled/); cleanup(); }); @@ -74,8 +88,8 @@ test("js/services/r2Service.js", async (t) => { const npub = "npub1test"; const credentials = { accountId: "acc123", - accessKeyId: "key123", - secretAccessKey: "secret123", + accessKeyId: "", + secretAccessKey: "", baseDomain: "https://pub-xxx.r2.dev", bucket: "custom-bucket", isLegacy: true, @@ -87,7 +101,70 @@ test("js/services/r2Service.js", async (t) => { assert.strictEqual(result.customDomainStatus, "manual"); }); - await t.test("resolveConnection - Returns Legacy Settings if no StorageService", async () => { + await t.test("ensureBucketConfigForNpub - Uses Meta Bucket when Missing", async () => { + const npub = "npub1testmeta"; + const credentials = { + accountId: "acc456", + accessKeyId: "", + secretAccessKey: "", + baseDomain: "https://pub-meta.r2.dev", + meta: { + bucket: "meta-bucket", + }, + isLegacy: false, + }; + + const result = await r2Service.ensureBucketConfigForNpub(npub, { credentials }); + assert.strictEqual(result.entry.bucket, "meta-bucket"); + }); + + await t.test("resolveConnection - Maps meta bucket into settings", async () => { + const npub = "npub1metaresolve"; + const pubkeyHex = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcd"; + + globalThis.window.NostrTools = { + nip19: { + decode(value) { + if (value === npub) { + return { type: "npub", data: pubkeyHex }; + } + throw new Error("Unexpected npub"); + }, + }, + }; + + storageService.listConnections = async () => [ + { + id: "default", + provider: "cloudflare_r2", + meta: { + defaultForUploads: true, + bucket: "meta-bucket", + publicBaseUrl: "https://pub-meta.r2.dev", + accountId: "acc456", + }, + }, + ]; + storageService.isUnlocked = () => true; + storageService.getConnection = async () => ({ + provider: "cloudflare_r2", + accountId: "acc456", + accessKeyId: "key456", + secretAccessKey: "secret456", + baseDomain: "https://pub-meta.r2.dev", + publicBaseUrl: "https://pub-meta.r2.dev", + meta: { + bucket: "meta-bucket", + publicBaseUrl: "https://pub-meta.r2.dev", + }, + }); + + const resolved = await r2Service.resolveConnection(npub); + assert.ok(resolved); + assert.strictEqual(resolved.bucket, "meta-bucket"); + }); + + await t.test("resolveConnection - Returns null without StorageService entries", async () => { const input = { accountId: "legacyAcc", accessKeyId: "legacyKey", @@ -99,8 +176,6 @@ test("js/services/r2Service.js", async (t) => { const npub = "npub1any"; const resolved = await r2Service.resolveConnection(npub); - assert.ok(resolved); - assert.strictEqual(resolved.accountId, "legacyAcc"); - assert.strictEqual(resolved.isLegacy, true); + assert.strictEqual(resolved, null); }); });