mirror of
https://github.com/run-llama/auto_rfp.git
synced 2026-07-01 21:54:05 -04:00
1151372e5c
* Add agentic mode support for LlamaParse and fix eligibility extraction
- Add agenticMode option to ParseOptions interface and Zod schema
- Enable agentic parsing by default for better multi-sheet Excel support
- Fix eligibility extraction to gracefully handle documents without
eligibility criteria by returning empty array instead of throwing error
* Refactor project index selection to single-select with auto-save
- Change ProjectIndexSelector from multi-select checkboxes to single-select
clickable rows with radio-style indicators
- Implement debounced auto-save (800ms) when selection changes
- Remove Edit/Save/Cancel buttons in favor of automatic persistence
- Add refreshKey prop to ProjectDocuments for efficient refetch on changes
- Update DocumentsSection to wire components with onSaveSuccess callback
- Simplify Questions page index selector to show index name with Active badge
* Fix ESLint configuration and resolve all lint errors
- Add eslint and eslint-config-next as dev dependencies
- Create .eslintrc.json with next/core-web-vitals preset
- Fix 43 unescaped entity errors by replacing quotes and apostrophes
with HTML entities across 14 component files
- Fix 9 react-hooks/exhaustive-deps warnings by wrapping fetch
functions in useCallback with proper dependency arrays
* Add Vitest testing infrastructure with 173 unit tests
- Configure Vitest with coverage reporting and path aliases
- Add test scripts (test, test:run, test:coverage) to package.json
- Install vitest, @vitest/coverage-v8, vite-tsconfig-paths, vitest-mock-extended
Test coverage includes:
- validators: extract-questions, generate-response, llamaparse, multi-step-response
- errors: all API error classes with type guard
- services: FileValidator (file type/size validation), DefaultResponseService
- middleware: apiHandler and withApiHandler request validation
Add mock infrastructure for Prisma, OpenAI, and test fixtures
* Fix missing agenticMode in LlamaParse processing service
Add agentic_mode field to request schema and form data parser to ensure
the agenticMode option is properly passed through the parsing pipeline.
This resolves TypeScript compilation error where agenticMode was required
in LlamaParseOptions but not provided by the processing service.
* Fix Add Manually button routing and improve Vercel preview URL handling
- Fix 404 error when clicking Add Manually button by using correct route path
/projects/{projectId}/questions/create instead of /questions/create?projectId=
- Update magic link auth to use VERCEL_URL for preview deployments
* Simplify IndexSelector to display-only mode
Remove index configuration UI since only single file selection is now supported:
- Remove Configure/Hide toggle button and collapsible panel
- Remove Select All/Deselect All functionality
- Remove checkbox selection grid for indexes
- Clean up handleIndexToggle and handleSelectAllIndexes from provider
- Keep project card display showing active index name and status
217 lines
6.9 KiB
TypeScript
217 lines
6.9 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import {
|
|
ApiError,
|
|
ValidationError,
|
|
AuthorizationError,
|
|
ForbiddenError,
|
|
NotFoundError,
|
|
ExternalServiceError,
|
|
LlamaCloudConnectionError,
|
|
AIServiceError,
|
|
DatabaseError,
|
|
ConfigurationError,
|
|
isApiError,
|
|
} from '@/lib/errors/api-errors';
|
|
|
|
describe('ApiError', () => {
|
|
it('should create error with all properties', () => {
|
|
const error = new ApiError('Test error', 500, 'TEST_ERROR');
|
|
expect(error.message).toBe('Test error');
|
|
expect(error.statusCode).toBe(500);
|
|
expect(error.code).toBe('TEST_ERROR');
|
|
expect(error.name).toBe('ApiError');
|
|
});
|
|
|
|
it('should extend Error', () => {
|
|
const error = new ApiError('Test', 400);
|
|
expect(error).toBeInstanceOf(Error);
|
|
});
|
|
|
|
it('should work without code', () => {
|
|
const error = new ApiError('Test', 400);
|
|
expect(error.code).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
describe('ValidationError', () => {
|
|
it('should have correct status code and code', () => {
|
|
const error = new ValidationError('Invalid input');
|
|
expect(error.statusCode).toBe(400);
|
|
expect(error.code).toBe('VALIDATION_ERROR');
|
|
expect(error.name).toBe('ValidationError');
|
|
});
|
|
|
|
it('should store validation details', () => {
|
|
const details = [{ field: 'email', message: 'Invalid email' }];
|
|
const error = new ValidationError('Validation failed', details);
|
|
expect(error.details).toEqual(details);
|
|
});
|
|
|
|
it('should extend ApiError', () => {
|
|
const error = new ValidationError('Test');
|
|
expect(error).toBeInstanceOf(ApiError);
|
|
});
|
|
});
|
|
|
|
describe('AuthorizationError', () => {
|
|
it('should have correct status code and code', () => {
|
|
const error = new AuthorizationError();
|
|
expect(error.statusCode).toBe(401);
|
|
expect(error.code).toBe('UNAUTHORIZED');
|
|
expect(error.name).toBe('AuthorizationError');
|
|
});
|
|
|
|
it('should use default message', () => {
|
|
const error = new AuthorizationError();
|
|
expect(error.message).toBe('Unauthorized');
|
|
});
|
|
|
|
it('should allow custom message', () => {
|
|
const error = new AuthorizationError('Token expired');
|
|
expect(error.message).toBe('Token expired');
|
|
});
|
|
});
|
|
|
|
describe('ForbiddenError', () => {
|
|
it('should have correct status code and code', () => {
|
|
const error = new ForbiddenError();
|
|
expect(error.statusCode).toBe(403);
|
|
expect(error.code).toBe('FORBIDDEN');
|
|
expect(error.name).toBe('ForbiddenError');
|
|
});
|
|
|
|
it('should use default message', () => {
|
|
const error = new ForbiddenError();
|
|
expect(error.message).toBe('Access denied');
|
|
});
|
|
|
|
it('should allow custom message', () => {
|
|
const error = new ForbiddenError('Admin access required');
|
|
expect(error.message).toBe('Admin access required');
|
|
});
|
|
});
|
|
|
|
describe('NotFoundError', () => {
|
|
it('should have correct status code and code', () => {
|
|
const error = new NotFoundError();
|
|
expect(error.statusCode).toBe(404);
|
|
expect(error.code).toBe('NOT_FOUND');
|
|
expect(error.name).toBe('NotFoundError');
|
|
});
|
|
|
|
it('should use default message', () => {
|
|
const error = new NotFoundError();
|
|
expect(error.message).toBe('Resource not found');
|
|
});
|
|
|
|
it('should allow custom message', () => {
|
|
const error = new NotFoundError('Project not found');
|
|
expect(error.message).toBe('Project not found');
|
|
});
|
|
});
|
|
|
|
describe('ExternalServiceError', () => {
|
|
it('should have correct status code and code', () => {
|
|
const error = new ExternalServiceError('Service unavailable', 'OpenAI');
|
|
expect(error.statusCode).toBe(502);
|
|
expect(error.code).toBe('EXTERNAL_SERVICE_ERROR');
|
|
expect(error.name).toBe('ExternalServiceError');
|
|
});
|
|
|
|
it('should store service name', () => {
|
|
const error = new ExternalServiceError('Error', 'LlamaCloud');
|
|
expect(error.service).toBe('LlamaCloud');
|
|
});
|
|
});
|
|
|
|
describe('LlamaCloudConnectionError', () => {
|
|
it('should have correct status code and code', () => {
|
|
const error = new LlamaCloudConnectionError();
|
|
expect(error.statusCode).toBe(502);
|
|
expect(error.code).toBe('EXTERNAL_SERVICE_ERROR');
|
|
expect(error.name).toBe('LlamaCloudConnectionError');
|
|
});
|
|
|
|
it('should use default message', () => {
|
|
const error = new LlamaCloudConnectionError();
|
|
expect(error.message).toBe('LlamaCloud connection failed');
|
|
});
|
|
|
|
it('should allow custom message', () => {
|
|
const error = new LlamaCloudConnectionError('API key invalid');
|
|
expect(error.message).toBe('API key invalid');
|
|
});
|
|
|
|
it('should have LlamaCloud as service', () => {
|
|
const error = new LlamaCloudConnectionError();
|
|
expect(error.service).toBe('LlamaCloud');
|
|
});
|
|
|
|
it('should extend ExternalServiceError', () => {
|
|
const error = new LlamaCloudConnectionError();
|
|
expect(error).toBeInstanceOf(ExternalServiceError);
|
|
});
|
|
});
|
|
|
|
describe('AIServiceError', () => {
|
|
it('should have correct default status code and code', () => {
|
|
const error = new AIServiceError('AI operation failed');
|
|
expect(error.statusCode).toBe(500);
|
|
expect(error.code).toBe('AI_SERVICE_ERROR');
|
|
});
|
|
|
|
it('should allow custom status code', () => {
|
|
const error = new AIServiceError('Rate limited', 429);
|
|
expect(error.statusCode).toBe(429);
|
|
});
|
|
});
|
|
|
|
describe('DatabaseError', () => {
|
|
it('should have correct default status code and code', () => {
|
|
const error = new DatabaseError('Query failed');
|
|
expect(error.statusCode).toBe(500);
|
|
expect(error.code).toBe('DATABASE_ERROR');
|
|
});
|
|
|
|
it('should allow custom status code', () => {
|
|
const error = new DatabaseError('Connection timeout', 503);
|
|
expect(error.statusCode).toBe(503);
|
|
});
|
|
});
|
|
|
|
describe('ConfigurationError', () => {
|
|
it('should have correct status code and code', () => {
|
|
const error = new ConfigurationError('Missing API key');
|
|
expect(error.statusCode).toBe(500);
|
|
expect(error.code).toBe('CONFIGURATION_ERROR');
|
|
expect(error.name).toBe('ConfigurationError');
|
|
});
|
|
});
|
|
|
|
describe('isApiError', () => {
|
|
it('should return true for ApiError instances', () => {
|
|
expect(isApiError(new ApiError('test', 500))).toBe(true);
|
|
expect(isApiError(new ValidationError('test'))).toBe(true);
|
|
expect(isApiError(new AuthorizationError())).toBe(true);
|
|
expect(isApiError(new ForbiddenError())).toBe(true);
|
|
expect(isApiError(new NotFoundError())).toBe(true);
|
|
expect(isApiError(new ExternalServiceError('test', 'service'))).toBe(true);
|
|
expect(isApiError(new LlamaCloudConnectionError())).toBe(true);
|
|
expect(isApiError(new AIServiceError('test'))).toBe(true);
|
|
expect(isApiError(new DatabaseError('test'))).toBe(true);
|
|
expect(isApiError(new ConfigurationError('test'))).toBe(true);
|
|
});
|
|
|
|
it('should return false for regular Error', () => {
|
|
expect(isApiError(new Error('test'))).toBe(false);
|
|
});
|
|
|
|
it('should return false for non-error values', () => {
|
|
expect(isApiError(null)).toBe(false);
|
|
expect(isApiError(undefined)).toBe(false);
|
|
expect(isApiError('error string')).toBe(false);
|
|
expect(isApiError({ message: 'error object' })).toBe(false);
|
|
expect(isApiError(123)).toBe(false);
|
|
});
|
|
});
|