Files
metabuilder/pastebin/tests/unit/hooks/useStorageConfig.test.ts
johndoe6345789 73c8e3d4dc feat: Add snippet-pastebin application
Full-featured pastebin application with:
- Next.js frontend with TypeScript
- Express backend with SQLite/PostgreSQL
- Syntax highlighting for 100+ languages
- Code quality validation system
- Comprehensive accessibility (WCAG compliance)
- Docker deployment configuration
- Playwright E2E tests
- Jest unit tests

This provides a standalone web application that can be
integrated as a capability module in the Universal Platform.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 16:54:54 +00:00

377 lines
11 KiB
TypeScript

/**
* Unit Tests for useStorageConfig Hook
* Tests storage backend configuration and Flask connection management
*/
import { renderHook, act } from '@testing-library/react';
import { useStorageConfig } from '@/hooks/useStorageConfig';
import * as storage from '@/lib/storage';
import { toast } from 'sonner';
jest.mock('@/lib/storage');
jest.mock('sonner');
describe('useStorageConfig Hook', () => {
beforeEach(() => {
jest.clearAllMocks();
delete process.env.NEXT_PUBLIC_FLASK_BACKEND_URL;
});
describe('initial state', () => {
it('should initialize with default values', () => {
const { result } = renderHook(() => useStorageConfig());
expect(result.current.storageBackend).toBe('indexeddb');
expect(result.current.flaskUrl).toBe('');
expect(result.current.flaskConnectionStatus).toBe('unknown');
expect(result.current.testingConnection).toBe(false);
expect(result.current.envVarSet).toBe(false);
});
});
describe('loadConfig', () => {
it('should load configuration from storage', () => {
(storage.loadStorageConfig as jest.Mock).mockReturnValue({
backend: 'flask',
flaskUrl: 'http://localhost:5000',
});
const { result } = renderHook(() => useStorageConfig());
act(() => {
result.current.loadConfig();
});
expect(result.current.storageBackend).toBe('flask');
expect(result.current.flaskUrl).toBe('http://localhost:5000');
});
it('should detect environment variable', () => {
process.env.NEXT_PUBLIC_FLASK_BACKEND_URL = 'http://api.example.com';
(storage.loadStorageConfig as jest.Mock).mockReturnValue({
backend: 'indexeddb',
});
const { result } = renderHook(() => useStorageConfig());
act(() => {
result.current.loadConfig();
});
expect(result.current.envVarSet).toBe(true);
});
it('should use env var as default Flask URL', () => {
process.env.NEXT_PUBLIC_FLASK_BACKEND_URL = 'http://api.example.com';
(storage.loadStorageConfig as jest.Mock).mockReturnValue({
backend: 'indexeddb',
});
const { result } = renderHook(() => useStorageConfig());
act(() => {
result.current.loadConfig();
});
expect(result.current.flaskUrl).toBe('http://api.example.com');
});
it('should use fallback URL if no config or env var', () => {
(storage.loadStorageConfig as jest.Mock).mockReturnValue({
backend: 'indexeddb',
});
const { result } = renderHook(() => useStorageConfig());
act(() => {
result.current.loadConfig();
});
expect(result.current.flaskUrl).toBe('http://localhost:5000');
});
});
describe('testFlaskConnection', () => {
it('should test Flask connection successfully', async () => {
const mockAdapter = {
testConnection: jest.fn().mockResolvedValue(true),
};
(storage.FlaskStorageAdapter as any) = jest.fn(() => mockAdapter);
const { result } = renderHook(() => useStorageConfig());
let connected = false;
await act(async () => {
connected = await result.current.testFlaskConnection('http://localhost:5000');
});
expect(connected).toBe(true);
expect(result.current.flaskConnectionStatus).toBe('connected');
});
it('should handle failed connection', async () => {
const mockAdapter = {
testConnection: jest.fn().mockResolvedValue(false),
};
(storage.FlaskStorageAdapter as any) = jest.fn(() => mockAdapter);
const { result } = renderHook(() => useStorageConfig());
let connected = false;
await act(async () => {
connected = await result.current.testFlaskConnection('http://localhost:5000');
});
expect(connected).toBe(false);
expect(result.current.flaskConnectionStatus).toBe('failed');
});
it('should handle adapter creation error', async () => {
(storage.FlaskStorageAdapter as any) = jest.fn(() => {
throw new Error('Invalid URL');
});
const { result } = renderHook(() => useStorageConfig());
let connected = false;
await act(async () => {
connected = await result.current.testFlaskConnection('invalid-url');
});
expect(connected).toBe(false);
expect(result.current.flaskConnectionStatus).toBe('failed');
});
it('should set testing flag', async () => {
const mockAdapter = {
testConnection: jest.fn().mockImplementation(() =>
new Promise(resolve => setTimeout(() => resolve(true), 100))
),
};
(storage.FlaskStorageAdapter as any) = jest.fn(() => mockAdapter);
const { result } = renderHook(() => useStorageConfig());
const testPromise = act(async () => {
await result.current.testFlaskConnection('http://localhost:5000');
});
expect(result.current.testingConnection).toBe(true);
await testPromise;
expect(result.current.testingConnection).toBe(false);
});
});
describe('handleTestConnection', () => {
it('should test connection using current Flask URL', async () => {
const mockAdapter = {
testConnection: jest.fn().mockResolvedValue(true),
};
(storage.FlaskStorageAdapter as any) = jest.fn(() => mockAdapter);
const { result } = renderHook(() => useStorageConfig());
act(() => {
result.current.setFlaskUrl('http://test.com:5000');
});
await act(async () => {
await result.current.handleTestConnection();
});
expect((storage.FlaskStorageAdapter as any)).toHaveBeenCalledWith('http://test.com:5000');
});
});
describe('handleSaveStorageConfig', () => {
it('should save indexeddb config without testing', async () => {
(storage.saveStorageConfig as jest.Mock).mockImplementation();
const { result } = renderHook(() => useStorageConfig());
act(() => {
result.current.setStorageBackend('indexeddb');
});
await act(async () => {
await result.current.handleSaveStorageConfig();
});
expect(storage.saveStorageConfig).toHaveBeenCalledWith({
backend: 'indexeddb',
flaskUrl: undefined,
});
expect(toast.success).toHaveBeenCalled();
});
it('should test Flask connection before saving', async () => {
const mockAdapter = {
testConnection: jest.fn().mockResolvedValue(true),
};
(storage.FlaskStorageAdapter as any) = jest.fn(() => mockAdapter);
(storage.saveStorageConfig as jest.Mock).mockImplementation();
const { result } = renderHook(() => useStorageConfig());
act(() => {
result.current.setStorageBackend('flask');
result.current.setFlaskUrl('http://localhost:5000');
});
await act(async () => {
await result.current.handleSaveStorageConfig();
});
expect(storage.saveStorageConfig).toHaveBeenCalled();
});
it('should reject if Flask URL is empty', async () => {
const { result } = renderHook(() => useStorageConfig());
act(() => {
result.current.setStorageBackend('flask');
result.current.setFlaskUrl('');
});
await act(async () => {
await result.current.handleSaveStorageConfig();
});
expect(toast.error).toHaveBeenCalledWith('Please enter a Flask backend URL');
expect(storage.saveStorageConfig).not.toHaveBeenCalled();
});
it('should reject if Flask connection fails', async () => {
const mockAdapter = {
testConnection: jest.fn().mockResolvedValue(false),
};
(storage.FlaskStorageAdapter as any) = jest.fn(() => mockAdapter);
const { result } = renderHook(() => useStorageConfig());
act(() => {
result.current.setStorageBackend('flask');
result.current.setFlaskUrl('http://localhost:5000');
});
await act(async () => {
await result.current.handleSaveStorageConfig();
});
expect(toast.error).toHaveBeenCalledWith(
'Cannot connect to Flask backend. Please check the URL and ensure the server is running.'
);
expect(storage.saveStorageConfig).not.toHaveBeenCalled();
});
it('should call onSuccess callback if provided', async () => {
(storage.saveStorageConfig as jest.Mock).mockImplementation();
const onSuccess = jest.fn().mockResolvedValue(undefined);
const { result } = renderHook(() => useStorageConfig());
act(() => {
result.current.setStorageBackend('indexeddb');
});
await act(async () => {
await result.current.handleSaveStorageConfig(onSuccess);
});
expect(onSuccess).toHaveBeenCalled();
});
it('should not call onSuccess if save fails', async () => {
const onSuccess = jest.fn();
const { result } = renderHook(() => useStorageConfig());
act(() => {
result.current.setStorageBackend('flask');
result.current.setFlaskUrl('');
});
await act(async () => {
await result.current.handleSaveStorageConfig(onSuccess);
});
expect(onSuccess).not.toHaveBeenCalled();
});
});
describe('state setters', () => {
it('should update storage backend', () => {
const { result } = renderHook(() => useStorageConfig());
act(() => {
result.current.setStorageBackend('flask');
});
expect(result.current.storageBackend).toBe('flask');
});
it('should update Flask URL', () => {
const { result } = renderHook(() => useStorageConfig());
act(() => {
result.current.setFlaskUrl('http://api.example.com:5000');
});
expect(result.current.flaskUrl).toBe('http://api.example.com:5000');
});
it('should update connection status', () => {
const { result } = renderHook(() => useStorageConfig());
act(() => {
result.current.setFlaskConnectionStatus('connected');
});
expect(result.current.flaskConnectionStatus).toBe('connected');
});
});
describe('complex scenarios', () => {
it('should handle full save workflow', async () => {
const mockAdapter = {
testConnection: jest.fn().mockResolvedValue(true),
};
(storage.FlaskStorageAdapter as any) = jest.fn(() => mockAdapter);
(storage.saveStorageConfig as jest.Mock).mockImplementation();
const onSuccess = jest.fn().mockResolvedValue(undefined);
const { result } = renderHook(() => useStorageConfig());
act(() => {
result.current.loadConfig();
result.current.setStorageBackend('flask');
result.current.setFlaskUrl('http://localhost:5000');
});
await act(async () => {
await result.current.handleSaveStorageConfig(onSuccess);
});
expect(storage.saveStorageConfig).toHaveBeenCalled();
expect(onSuccess).toHaveBeenCalled();
expect(toast.success).toHaveBeenCalled();
});
it('should handle backend switching', () => {
const { result } = renderHook(() => useStorageConfig());
act(() => {
result.current.setStorageBackend('indexeddb');
});
expect(result.current.storageBackend).toBe('indexeddb');
act(() => {
result.current.setStorageBackend('flask');
});
expect(result.current.storageBackend).toBe('flask');
});
});
});