mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-08-28 14:18:04 -05:00
Fix flaky art upload E2E test by verifying MinIO bucket
The test failed intermittently in CI with a 500 from POST /art/new.data: the sendou bucket was missing for the whole run, so the upload threw NoSuchBucket. art.spec.ts is the only e2e test that writes to S3, which is why it was the only casualty.
This commit is contained in:
5
.github/workflows/e2e-tests.yml
vendored
5
.github/workflows/e2e-tests.yml
vendored
@@ -23,10 +23,13 @@ jobs:
|
||||
- name: Start MinIO
|
||||
run: docker compose up -d minio
|
||||
|
||||
# the bucket, not the health endpoint: MinIO answers health/live while it is still
|
||||
# bootstrapping, so a run whose bucket never got created would only surface much later
|
||||
# as a 500 in the one test that uploads an image
|
||||
- name: Wait for MinIO to be ready
|
||||
run: |
|
||||
for i in {1..30}; do
|
||||
if curl -sf http://127.0.0.1:9000/minio/health/live; then
|
||||
if curl -sf -o /dev/null http://127.0.0.1:9000/sendou/; then
|
||||
echo "MinIO is ready"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
@@ -12,9 +12,9 @@ services:
|
||||
isAlive() { curl -sf http://127.0.0.1:9000/minio/health/live; } # check if Minio is alive
|
||||
minio $$0 "$$@" --quiet & echo $$! > /tmp/minio.pid # start Minio in the background
|
||||
while ! isAlive; do sleep 0.1; done # wait until Minio is alive
|
||||
mc alias set minio http://127.0.0.1:9000 minio-user minio-password # setup Minio client
|
||||
mc mb minio/sendou || true # create a test bucket
|
||||
mc anonymous set public minio/sendou # make the test bucket public
|
||||
until mc alias set minio http://127.0.0.1:9000 minio-user minio-password; do sleep 0.5; done # setup Minio client (health/live can answer before credentials are accepted)
|
||||
until mc mb --ignore-existing minio/sendou; do sleep 0.5; done # create a test bucket
|
||||
until mc anonymous set public minio/sendou; do sleep 0.5; done # make the test bucket public
|
||||
kill -s INT $$(cat /tmp/minio.pid) && rm /tmp/minio.pid # stop Minio
|
||||
while isAlive; do sleep 0.1; done # wait until Minio is stopped
|
||||
exec minio $$0 "$$@" # start Minio in the foreground
|
||||
|
||||
@@ -7,6 +7,9 @@ import { E2E_BASE_PORT, e2eWorkerPort } from "./helpers/playwright";
|
||||
const DEBUG = process.env.E2E_DEBUG === "true";
|
||||
const SERVER_PROCESSES: ChildProcess[] = [];
|
||||
const MINIO_MARKER_FILE = ".e2e-minio-started";
|
||||
const STORAGE_BUCKET = "sendou";
|
||||
/** Anonymously listable only once the bucket exists and its public policy is set. */
|
||||
const MINIO_BUCKET_URL = `http://127.0.0.1:9000/${STORAGE_BUCKET}/`;
|
||||
const BUILD_MARKER_FILE = ".e2e-build-marker";
|
||||
const BUILD_INPUTS = [
|
||||
"app",
|
||||
@@ -21,16 +24,26 @@ declare global {
|
||||
var __E2E_SERVERS__: ChildProcess[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the image storage is usable, which takes more than MinIO answering its health check:
|
||||
* the container bootstraps the bucket only after startup, and a run whose bucket never got created
|
||||
* would otherwise fail deep inside the one test that uploads an image (`art.spec.ts`) with an
|
||||
* opaque 500.
|
||||
*/
|
||||
async function isMinioBucketReady(): Promise<boolean> {
|
||||
try {
|
||||
const response = await fetch(MINIO_BUCKET_URL);
|
||||
return response.ok;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function waitForMinio(timeout = 60000): Promise<boolean> {
|
||||
const start = Date.now();
|
||||
while (Date.now() - start < timeout) {
|
||||
try {
|
||||
const response = await fetch("http://127.0.0.1:9000/minio/health/live");
|
||||
if (response.ok) {
|
||||
return true;
|
||||
}
|
||||
} catch {
|
||||
// MinIO not ready yet
|
||||
if (await isMinioBucketReady()) {
|
||||
return true;
|
||||
}
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
}
|
||||
@@ -39,15 +52,10 @@ async function waitForMinio(timeout = 60000): Promise<boolean> {
|
||||
|
||||
async function ensureMinioRunning(): Promise<boolean> {
|
||||
// Check if MinIO is already running
|
||||
try {
|
||||
const response = await fetch("http://127.0.0.1:9000/minio/health/live");
|
||||
if (response.ok) {
|
||||
// biome-ignore lint/suspicious/noConsole: CLI script output
|
||||
console.log("MinIO is already running");
|
||||
return false;
|
||||
}
|
||||
} catch {
|
||||
// MinIO not running, we need to start it
|
||||
if (await isMinioBucketReady()) {
|
||||
// biome-ignore lint/suspicious/noConsole: CLI script output
|
||||
console.log("MinIO is already running");
|
||||
return false;
|
||||
}
|
||||
|
||||
// biome-ignore lint/suspicious/noConsole: CLI script output
|
||||
@@ -56,7 +64,9 @@ async function ensureMinioRunning(): Promise<boolean> {
|
||||
|
||||
const isReady = await waitForMinio();
|
||||
if (!isReady) {
|
||||
throw new Error("MinIO failed to start within timeout");
|
||||
throw new Error(
|
||||
`MinIO did not become usable within timeout (${MINIO_BUCKET_URL} never answered OK). If MinIO is running, its "${STORAGE_BUCKET}" bucket is missing or not public — recreate the container with "docker compose up -d --force-recreate minio".`,
|
||||
);
|
||||
}
|
||||
|
||||
// biome-ignore lint/suspicious/noConsole: CLI script output
|
||||
@@ -225,7 +235,7 @@ async function globalSetup(config: FullConfig) {
|
||||
STORAGE_ACCESS_KEY: "minio-user",
|
||||
STORAGE_SECRET: "minio-password",
|
||||
STORAGE_REGION: "us-east-1",
|
||||
STORAGE_BUCKET: "sendou",
|
||||
STORAGE_BUCKET,
|
||||
// no system messages to a shared skalop instance (see build env above)
|
||||
SKALOP_SYSTEM_MESSAGE_URL: "",
|
||||
SKALOP_TOKEN: "",
|
||||
|
||||
Reference in New Issue
Block a user