test: stabilize e2e auth and rate limits

This commit is contained in:
Adrian Acala
2026-01-18 21:22:03 -08:00
parent 15ac634d15
commit 260a898e3e
13 changed files with 250 additions and 79 deletions
+35 -4
View File
@@ -1,5 +1,5 @@
import React, { createContext, useContext, useState, useCallback, type ReactNode } from 'react';
import { importDrawings } from '../utils/importUtils';
import { importDrawings, type ImportResult } from '../utils/importUtils';
export type UploadStatus = 'pending' | 'uploading' | 'processing' | 'success' | 'error';
@@ -11,9 +11,35 @@ export interface UploadTask {
error?: string;
}
const generateUploadId = (): string => {
const cryptoObj: Crypto | undefined =
typeof globalThis !== "undefined"
? globalThis.crypto || (globalThis as any).msCrypto
: undefined;
if (cryptoObj?.randomUUID) {
return cryptoObj.randomUUID();
}
if (cryptoObj?.getRandomValues) {
const bytes = new Uint8Array(16);
cryptoObj.getRandomValues(bytes);
bytes[6] = (bytes[6] & 0x0f) | 0x40; // RFC 4122 version 4
bytes[8] = (bytes[8] & 0x3f) | 0x80; // RFC 4122 variant
const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0"));
return `${hex.slice(0, 4).join("")}-${hex.slice(4, 6).join("")}-${hex
.slice(6, 8)
.join("")}-${hex.slice(8, 10).join("")}-${hex.slice(10).join("")}`;
}
return `upload-${Date.now().toString(16)}-${Math.random()
.toString(16)
.slice(2)}`;
};
interface UploadContextType {
tasks: UploadTask[];
uploadFiles: (files: File[], targetCollectionId: string | null) => Promise<void>;
uploadFiles: (files: File[], targetCollectionId: string | null) => Promise<ImportResult>;
clearCompleted: () => void;
removeTask: (id: string) => void;
isUploading: boolean;
@@ -48,7 +74,7 @@ export const UploadProvider: React.FC<{ children: ReactNode }> = ({ children })
const uploadFiles = useCallback(async (files: File[], targetCollectionId: string | null) => {
const newTasks: UploadTask[] = files.map(f => ({
id: crypto.randomUUID(),
id: generateUploadId(),
fileName: f.name,
status: 'pending',
progress: 0
@@ -68,13 +94,18 @@ export const UploadProvider: React.FC<{ children: ReactNode }> = ({ children })
};
try {
await importDrawings(files, targetCollectionId, undefined, handleProgress);
return await importDrawings(files, targetCollectionId, undefined, handleProgress);
} catch (e) {
console.error("Global upload error", e);
// Mark all new tasks as error if something crashed completely
newTasks.forEach(t => {
updateTask(t.id, { status: 'error', error: 'Upload failed unexpectedly' });
});
return {
success: 0,
failed: newTasks.length,
errors: ['Upload failed unexpectedly'],
};
}
}, [updateTask]);
+24 -4
View File
@@ -306,10 +306,20 @@ export const Dashboard: React.FC = () => {
const targetCollectionId = selectedCollectionId === undefined ? null : selectedCollectionId;
// Use the global upload context
uploadFiles(fileArray, targetCollectionId).finally(() => {
try {
const result = await uploadFiles(fileArray, targetCollectionId);
if (result.failed > 0) {
setShowImportError({
isOpen: true,
message: result.errors.length > 0
? result.errors.join("\n")
: "Some files failed to import.",
});
}
} finally {
// Refresh after all uploads complete (success or failure)
refreshData();
});
}
};
const handleRenameDrawing = async (id: string, name: string) => {
@@ -532,9 +542,19 @@ export const Dashboard: React.FC = () => {
const drawingFiles = files.filter(f => !f.name.endsWith('.excalidrawlib'));
if (drawingFiles.length > 0) {
uploadFiles(drawingFiles, targetCollectionId).finally(() => {
try {
const result = await uploadFiles(drawingFiles, targetCollectionId);
if (result.failed > 0) {
setShowImportError({
isOpen: true,
message: result.errors.length > 0
? result.errors.join("\n")
: "Some files failed to import.",
});
}
} finally {
refreshData();
});
}
}
return;
+6
View File
@@ -2,6 +2,12 @@ import { exportToSvg } from "@excalidraw/excalidraw";
import { api } from "../api";
import { type UploadStatus } from "../context/UploadContext";
export type ImportResult = {
success: number;
failed: number;
errors: string[];
};
export const importDrawings = async (
files: File[],
targetCollectionId: string | null,