mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-07 03:15:56 +00:00
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
121 lines
5.8 KiB
JavaScript
121 lines
5.8 KiB
JavaScript
// SeaweedFS object-store scenario for Fider.
|
|
//
|
|
// Fider is configured with the S3 blob backend (env.j2: BLOB_STORAGE=s3 +
|
|
// BLOB_STORAGE_S3_BUCKET / _ACCESS_KEY_ID / _SECRET_ACCESS_KEY /
|
|
// _ENDPOINT_URL / _REGION), so a tenant logo the administrator uploads on the
|
|
// admin General settings page is written to the consumer bucket as a new
|
|
// private blob (Fider stores blobs with ACL=private and reverse-proxies them
|
|
// through its own /images route, which is why the role declares no public
|
|
// flag). The action signs the administrator in through Fider's SSO button +
|
|
// Keycloak form, opens the admin General settings page, sets the "Your Logo"
|
|
// file input to a small PNG and clicks Save; the shared check proves the
|
|
// bucket grew via the Filer UI.
|
|
//
|
|
// Required env (rendered by templates/playwright.env.j2):
|
|
// FIDER_BASE_URL, the OIDC login vars consumed by performKeycloakLoginForm,
|
|
// and the SEAWEEDFS_* keys consumed by runSeaweedfsStorageCheck.
|
|
|
|
const { test, expect } = require("@playwright/test");
|
|
const { resolveTimeout, isOnionTarget } = require("./timeouts");
|
|
const { skipUnlessServiceEnabled } = require("./service-gating");
|
|
const { runSeaweedfsStorageCheck, performKeycloakLoginForm, decodeDotenvQuotedValue } = require("./personas");
|
|
|
|
const LOGO_PNG = Buffer.from(
|
|
"iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAIAAAAiOjnJAAABeElEQVR42u3SMQ0AAAgEsVeHMDQhEBMM" +
|
|
"DE2q4HKpHjgXCTAWxsJYYCyMhbHAWBgLY4GxMBbGAmNhLIwFxsJYGAuMhbEwFhgLY2EsMBbGwlhgLIyF" +
|
|
"scBYGAtjgbEwFsYCY2EsjAXGwlgYC4yFsTAWGAtjYSwwFsbCWGAsjIWxwFgYC2OBsTAWxgJjYSyMBcbC" +
|
|
"WBgLjIWxMBYYC2NhLDAWxsJYYCyMhbHAWBgLY4GxMBbGAmNhLIyFsVTAWBgLY4GxMBbGAmNhLIwFxsJY" +
|
|
"GAuMhbEwFhgLY2EsMBbGwlhgLIyFscBYGAtjgbEwFsYCY2EsjAXGwlgYC4yFsTAWGAtjYSwwFsbCWGAs" +
|
|
"jIWxwFgYC2OBsTAWxgJjYSyMBcbCWBgLjIWxMBYYC2NhLDAWxsJYYCyMhbHAWBgLY4GxMBbGAmNhLIyF" +
|
|
"sVTAWBgLY4GxMBbGAmNhLIwFxsJYGAtjqYCxMBbGAmNhLIwFxsJYGAuMhbEwFhgLY2EsMBbGwlhgLIyF" +
|
|
"scBYGAtjgbH4ZgHTqvyKw+KRzwAAAABJRU5ErkJggg==",
|
|
"base64",
|
|
);
|
|
|
|
async function clickFiderSsoButton(locator) {
|
|
const signInLink = locator
|
|
.getByRole("button", { name: /sign in/i })
|
|
.or(locator.getByRole("link", { name: /sign in/i }));
|
|
await signInLink.first().waitFor({ state: "visible", timeout: resolveTimeout(30_000) });
|
|
await signInLink.first().click();
|
|
|
|
const ssoButton = locator.getByRole("link", { name: /continue with/i });
|
|
await ssoButton.first().waitFor({ state: "visible", timeout: resolveTimeout(15_000) });
|
|
await ssoButton.first().click({ force: true, timeout: resolveTimeout(30_000) });
|
|
}
|
|
|
|
test.use({ ignoreHTTPSErrors: true });
|
|
|
|
test("seaweedfs: an uploaded Fider tenant logo is stored in the SeaweedFS bucket", async ({ page, browser }) => {
|
|
test.skip(isOnionTarget(), "SeaweedFS filer UI is not a Tor surface on an onion node (headless backend)");
|
|
skipUnlessServiceEnabled("seaweedfs");
|
|
test.setTimeout(resolveTimeout(180_000));
|
|
|
|
const fiderBaseUrl = decodeDotenvQuotedValue(process.env.FIDER_BASE_URL || "");
|
|
const adminUsername = decodeDotenvQuotedValue(process.env.ADMIN_USERNAME || "");
|
|
const adminPassword = decodeDotenvQuotedValue(process.env.ADMIN_PASSWORD || "");
|
|
|
|
await runSeaweedfsStorageCheck(page, browser, {
|
|
label: "a Fider admin tenant-logo upload",
|
|
expectInPlaceOverwrite: true,
|
|
action: async (appPage) => {
|
|
expect(fiderBaseUrl, "FIDER_BASE_URL must be set for the Fider seaweedfs check").toBeTruthy();
|
|
expect(adminUsername, "ADMIN_USERNAME must be set for the Fider seaweedfs check").toBeTruthy();
|
|
expect(adminPassword, "ADMIN_PASSWORD must be set for the Fider seaweedfs check").toBeTruthy();
|
|
|
|
const baseUrl = fiderBaseUrl.replace(/\/$/, "");
|
|
|
|
await appPage.goto(`${baseUrl}/`, { waitUntil: "domcontentloaded" });
|
|
await clickFiderSsoButton(appPage);
|
|
await performKeycloakLoginForm(appPage, adminUsername, adminPassword);
|
|
|
|
await expect(
|
|
appPage.locator(".c-menu-user").first(),
|
|
"the administrator must be authenticated before opening the admin settings",
|
|
).toBeVisible({ timeout: resolveTimeout(60_000) });
|
|
|
|
await appPage.goto(`${baseUrl}/admin`, { waitUntil: "domcontentloaded" });
|
|
|
|
const fileInput = appPage.locator('input[type="file"][accept="image/*"]').first();
|
|
await expect(
|
|
fileInput,
|
|
"the Fider admin General settings page must expose the 'Your Logo' image input",
|
|
).toBeAttached({ timeout: resolveTimeout(60_000) });
|
|
|
|
const marker = `infinito-storage-check-${Date.now()}.png`;
|
|
await fileInput.setInputFiles({
|
|
name: marker,
|
|
mimeType: "image/png",
|
|
buffer: LOGO_PNG,
|
|
});
|
|
|
|
await expect(
|
|
appPage.locator('.c-image-upload img[src^="data:image"], .preview img[src^="data:image"]').first(),
|
|
"the selected logo must be loaded into the form (preview rendered) before saving",
|
|
).toBeVisible({ timeout: resolveTimeout(30_000) });
|
|
|
|
const saveButton = appPage.getByRole("button", { name: /^save$/i }).first();
|
|
await expect(
|
|
saveButton,
|
|
"the Fider admin General settings page must expose a Save action that persists the logo",
|
|
).toBeVisible({ timeout: resolveTimeout(60_000) });
|
|
const [settingsResp] = await Promise.all([
|
|
appPage.waitForResponse(
|
|
(r) => /\/_api\/admin\/settings\/general/.test(r.url()) && r.request().method() === "POST",
|
|
{ timeout: resolveTimeout(60_000) },
|
|
),
|
|
saveButton.click(),
|
|
]);
|
|
expect(
|
|
settingsResp.ok(),
|
|
`Fider must accept the General-settings save that persists the logo (got HTTP ${settingsResp.status()})`,
|
|
).toBeTruthy();
|
|
const reqBody = settingsResp.request().postData() || "";
|
|
expect(
|
|
reqBody,
|
|
"the General-settings save must carry the logo upload payload (empty upload = silent no-op)",
|
|
).toMatch(/"upload"\s*:\s*\{[^}]*"content"/);
|
|
},
|
|
});
|
|
});
|