fix(tests): resolve broken test utils
This commit is contained in:
parent
a3f49f9bd7
commit
51fb3a7e48
|
|
@ -0,0 +1,62 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Final script to fix all remaining issues in documents_tests.rs
|
||||
"""
|
||||
|
||||
import re
|
||||
import sys
|
||||
|
||||
def fix_documents_tests(content):
|
||||
"""Fix all remaining issues in documents_tests.rs"""
|
||||
|
||||
# Fix 1: Replace user.id() with user.user_response.id (for TestUser objects)
|
||||
# This converts String to Uuid properly
|
||||
content = re.sub(r'(\w+)\.id\(\)', r'\1.user_response.id', content)
|
||||
|
||||
# Fix 2: Replace user.role with user.user_response.role (for TestUser objects)
|
||||
content = re.sub(r'(\w+)\.role\b', r'\1.user_response.role', content)
|
||||
|
||||
# Fix 3: Replace create_test_admin() with create_admin_user()
|
||||
content = re.sub(r'\.create_test_admin\(\)', '.create_admin_user()', content)
|
||||
|
||||
# Fix 4: Fix document.id() back to document.id (documents don't have id() method)
|
||||
content = re.sub(r'(doc\w*|document\w*|result\[\d+\]|deleted_doc|found_doc\.unwrap\(\))\.user_response\.id\b', r'\1.id', content)
|
||||
|
||||
# Fix 5: Fix response.id() to response.id for DocumentResponse
|
||||
content = re.sub(r'response\.user_response\.id\b', 'response.id', content)
|
||||
|
||||
# Fix 6: Fix any standalone .user_response.id calls that shouldn't be there
|
||||
content = re.sub(r'\.user_response\.id\(\)', '.user_response.id', content)
|
||||
|
||||
# Fix 7: Fix doubled "user_response" patterns
|
||||
content = re.sub(r'\.user_response\.user_response\.', '.user_response.', content)
|
||||
|
||||
return content
|
||||
|
||||
def main():
|
||||
file_path = '/root/repos/readur/src/tests/documents_tests.rs'
|
||||
|
||||
# Read the file
|
||||
try:
|
||||
with open(file_path, 'r') as f:
|
||||
content = f.read()
|
||||
except FileNotFoundError:
|
||||
print(f"Error: Could not find file {file_path}")
|
||||
return 1
|
||||
|
||||
# Apply fixes
|
||||
print("Applying final fixes to documents_tests.rs...")
|
||||
fixed_content = fix_documents_tests(content)
|
||||
|
||||
# Write back the fixed content
|
||||
try:
|
||||
with open(file_path, 'w') as f:
|
||||
f.write(fixed_content)
|
||||
print(f"Successfully applied fixes to {file_path}")
|
||||
return 0
|
||||
except Exception as e:
|
||||
print(f"Error writing file: {e}")
|
||||
return 1
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Fix models::User objects that were incorrectly converted to use .user_response
|
||||
"""
|
||||
|
||||
import re
|
||||
import sys
|
||||
|
||||
def fix_models_user(content):
|
||||
"""Fix models::User objects that were incorrectly converted"""
|
||||
|
||||
# Find all lines that create models::User objects via db.create_user()
|
||||
# and track the variable names
|
||||
user_vars = set()
|
||||
|
||||
lines = content.split('\n')
|
||||
for line in lines:
|
||||
if 'db.create_user(' in line and 'await' in line:
|
||||
# This creates a models::User object
|
||||
match = re.search(r'let (\w+) = .*db\.create_user\(', line)
|
||||
if match:
|
||||
user_vars.add(match.group(1))
|
||||
|
||||
# Now fix all references to these variables
|
||||
for var in user_vars:
|
||||
# Revert .user_response.id back to .id
|
||||
content = content.replace(f'{var}.user_response.id', f'{var}.id')
|
||||
# Revert .user_response.role back to .role
|
||||
content = content.replace(f'{var}.user_response.role', f'{var}.role')
|
||||
|
||||
return content
|
||||
|
||||
def main():
|
||||
file_path = '/root/repos/readur/src/tests/documents_tests.rs'
|
||||
|
||||
# Read the file
|
||||
try:
|
||||
with open(file_path, 'r') as f:
|
||||
content = f.read()
|
||||
except FileNotFoundError:
|
||||
print(f"Error: Could not find file {file_path}")
|
||||
return 1
|
||||
|
||||
# Apply fixes
|
||||
print("Fixing models::User objects...")
|
||||
fixed_content = fix_models_user(content)
|
||||
|
||||
# Write back the fixed content
|
||||
try:
|
||||
with open(file_path, 'w') as f:
|
||||
f.write(fixed_content)
|
||||
print(f"Successfully fixed {file_path}")
|
||||
return 0
|
||||
except Exception as e:
|
||||
print(f"Error writing file: {e}")
|
||||
return 1
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Final comprehensive fix for TestUser vs models::User distinction
|
||||
"""
|
||||
|
||||
import re
|
||||
import sys
|
||||
|
||||
def fix_user_object_types(content):
|
||||
"""Fix the distinction between TestUser and models::User objects"""
|
||||
|
||||
lines = content.split('\n')
|
||||
fixed_lines = []
|
||||
|
||||
# Track which variables are TestUser vs User objects
|
||||
testuser_vars = set()
|
||||
user_vars = set()
|
||||
|
||||
for i, line in enumerate(lines):
|
||||
# Identify TestUser variables (created by auth_helper methods)
|
||||
if re.search(r'let (\w+) = auth_helper\.create_test_user\(\)', line):
|
||||
var_name = re.search(r'let (\w+) = auth_helper\.create_test_user\(\)', line).group(1)
|
||||
testuser_vars.add(var_name)
|
||||
elif re.search(r'let (\w+) = auth_helper\.create_admin_user\(\)', line):
|
||||
var_name = re.search(r'let (\w+) = auth_helper\.create_admin_user\(\)', line).group(1)
|
||||
testuser_vars.add(var_name)
|
||||
elif re.search(r'let (\w+) = auth_helper\.create_test_admin\(\)', line):
|
||||
var_name = re.search(r'let (\w+) = auth_helper\.create_test_admin\(\)', line).group(1)
|
||||
testuser_vars.add(var_name)
|
||||
|
||||
# Identify models::User variables (created by db.create_user)
|
||||
elif re.search(r'let (\w+) = .*db\.create_user\(', line):
|
||||
var_name = re.search(r'let (\w+) = .*db\.create_user\(', line).group(1)
|
||||
user_vars.add(var_name)
|
||||
|
||||
# Fix the line based on variable types
|
||||
fixed_line = line
|
||||
|
||||
# For TestUser objects, ensure they use .user_response
|
||||
for var in testuser_vars:
|
||||
# Convert .id to .user_response.id for TestUser objects
|
||||
fixed_line = re.sub(rf'\b{var}\.id\b', f'{var}.user_response.id', fixed_line)
|
||||
# Convert .role to .user_response.role for TestUser objects
|
||||
fixed_line = re.sub(rf'\b{var}\.role\b', f'{var}.user_response.role', fixed_line)
|
||||
|
||||
# For models::User objects, ensure they use direct access
|
||||
for var in user_vars:
|
||||
# Remove .user_response for User objects
|
||||
fixed_line = re.sub(rf'\b{var}\.user_response\.id\b', f'{var}.id', fixed_line)
|
||||
fixed_line = re.sub(rf'\b{var}\.user_response\.role\b', f'{var}.role', fixed_line)
|
||||
|
||||
fixed_lines.append(fixed_line)
|
||||
|
||||
return '\n'.join(fixed_lines)
|
||||
|
||||
def main():
|
||||
file_path = '/root/repos/readur/src/tests/documents_tests.rs'
|
||||
|
||||
# Read the file
|
||||
try:
|
||||
with open(file_path, 'r') as f:
|
||||
content = f.read()
|
||||
except FileNotFoundError:
|
||||
print(f"Error: Could not find file {file_path}")
|
||||
return 1
|
||||
|
||||
# Apply fixes
|
||||
print("Applying comprehensive TestUser vs User fixes...")
|
||||
fixed_content = fix_user_object_types(content)
|
||||
|
||||
# Write back the fixed content
|
||||
try:
|
||||
with open(file_path, 'w') as f:
|
||||
f.write(fixed_content)
|
||||
print(f"Successfully fixed {file_path}")
|
||||
return 0
|
||||
except Exception as e:
|
||||
print(f"Error writing file: {e}")
|
||||
return 1
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Fix the distinction between models::User and TestUser objects
|
||||
"""
|
||||
|
||||
import re
|
||||
import sys
|
||||
|
||||
def fix_user_types(content):
|
||||
"""Fix the distinction between models::User and TestUser objects"""
|
||||
|
||||
# First, find all the places where we import or create Users vs TestUsers
|
||||
# and fix them appropriately
|
||||
|
||||
# In the test functions, we need to identify which variables are TestUser and which are User
|
||||
# Let's look for patterns that indicate TestUser creation
|
||||
|
||||
# Pattern 1: Variables created from auth_helper.create_test_user() are TestUser
|
||||
# Pattern 2: Variables created from auth_helper.create_admin_user() are TestUser
|
||||
# Pattern 3: Variables created from auth_helper.create_test_admin() are TestUser
|
||||
|
||||
# Find all test functions and fix them individually
|
||||
test_functions = re.findall(r'(#\[tokio::test\].*?^ })', content, re.MULTILINE | re.DOTALL)
|
||||
|
||||
for func in test_functions:
|
||||
# Check if this function creates TestUser objects
|
||||
if 'auth_helper.create_test_user()' in func or 'auth_helper.create_admin_user()' in func or 'auth_helper.create_test_admin()' in func:
|
||||
# This function uses TestUser objects, keep .user_response
|
||||
continue
|
||||
else:
|
||||
# This function might be using models::User objects, revert .user_response
|
||||
# But only if the variable is clearly a User object
|
||||
func_lines = func.split('\n')
|
||||
for i, line in enumerate(func_lines):
|
||||
# Look for variable declarations that create User objects
|
||||
if 'create_test_user(&' in line and 'UserRole::' in line:
|
||||
# This creates a models::User object
|
||||
var_match = re.search(r'let (\w+) = create_test_user\(', line)
|
||||
if var_match:
|
||||
var_name = var_match.group(1)
|
||||
# Replace .user_response with direct access for this variable
|
||||
func = func.replace(f'{var_name}.user_response.id', f'{var_name}.id')
|
||||
func = func.replace(f'{var_name}.user_response.role', f'{var_name}.role')
|
||||
|
||||
# Apply the fixed functions back to content
|
||||
# This is complex, so let's use a different approach
|
||||
|
||||
# Let's be more specific about which variables are TestUser vs User
|
||||
# Look for the specific patterns in the migration
|
||||
|
||||
# Fix models::User objects that got incorrectly converted
|
||||
# Pattern: Variables that are clearly User objects (not TestUser)
|
||||
lines = content.split('\n')
|
||||
in_test_function = False
|
||||
current_function_uses_testuser = False
|
||||
|
||||
fixed_lines = []
|
||||
|
||||
for line in lines:
|
||||
if '#[tokio::test]' in line:
|
||||
in_test_function = True
|
||||
current_function_uses_testuser = False
|
||||
elif in_test_function and line.strip() == '}':
|
||||
in_test_function = False
|
||||
current_function_uses_testuser = False
|
||||
elif in_test_function and ('auth_helper.create_test_user()' in line or 'auth_helper.create_admin_user()' in line or 'auth_helper.create_test_admin()' in line):
|
||||
current_function_uses_testuser = True
|
||||
elif in_test_function and not current_function_uses_testuser:
|
||||
# This function doesn't use TestUser objects, so revert .user_response
|
||||
# But only for variables that are created with the old pattern
|
||||
if 'create_test_user(&' in line and 'UserRole::' in line:
|
||||
# This line creates a models::User object
|
||||
var_match = re.search(r'let (\w+) = create_test_user\(', line)
|
||||
if var_match:
|
||||
var_name = var_match.group(1)
|
||||
# Mark this variable as a User object
|
||||
# We'll fix its usage in subsequent lines
|
||||
pass
|
||||
# Fix usage of User objects
|
||||
line = re.sub(r'(\w+)\.user_response\.id\b', r'\1.id', line)
|
||||
line = re.sub(r'(\w+)\.user_response\.role\b', r'\1.role', line)
|
||||
|
||||
fixed_lines.append(line)
|
||||
|
||||
return '\n'.join(fixed_lines)
|
||||
|
||||
def main():
|
||||
file_path = '/root/repos/readur/src/tests/documents_tests.rs'
|
||||
|
||||
# Read the file
|
||||
try:
|
||||
with open(file_path, 'r') as f:
|
||||
content = f.read()
|
||||
except FileNotFoundError:
|
||||
print(f"Error: Could not find file {file_path}")
|
||||
return 1
|
||||
|
||||
# Apply fixes
|
||||
print("Fixing User vs TestUser distinction...")
|
||||
fixed_content = fix_user_types(content)
|
||||
|
||||
# Write back the fixed content
|
||||
try:
|
||||
with open(file_path, 'w') as f:
|
||||
f.write(fixed_content)
|
||||
print(f"Successfully fixed {file_path}")
|
||||
return 0
|
||||
except Exception as e:
|
||||
print(f"Error writing file: {e}")
|
||||
return 1
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
import { describe, test, expect, vi } from 'vitest';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { ThemeProvider, createTheme } from '@mui/material/styles';
|
||||
import { describe, test, expect, vi, beforeEach } from 'vitest';
|
||||
import { screen } from '@testing-library/react';
|
||||
import NotificationPanel from '../NotificationPanel';
|
||||
import { NotificationProvider } from '../../../contexts/NotificationContext';
|
||||
import { renderWithProviders, setupTestEnvironment } from '../../../test/test-utils';
|
||||
import React from 'react';
|
||||
|
||||
// Mock date-fns
|
||||
|
|
@ -10,7 +10,6 @@ vi.mock('date-fns', () => ({
|
|||
formatDistanceToNow: vi.fn(() => '2 minutes ago'),
|
||||
}));
|
||||
|
||||
const theme = createTheme();
|
||||
|
||||
const createMockAnchorEl = () => {
|
||||
const mockEl = document.createElement('div');
|
||||
|
|
@ -28,13 +27,14 @@ const createMockAnchorEl = () => {
|
|||
};
|
||||
|
||||
describe('NotificationPanel - Simple Tests', () => {
|
||||
beforeEach(() => {
|
||||
setupTestEnvironment();
|
||||
});
|
||||
test('should not render when anchorEl is null', () => {
|
||||
const { container } = render(
|
||||
<ThemeProvider theme={theme}>
|
||||
<NotificationProvider>
|
||||
<NotificationPanel anchorEl={null} onClose={vi.fn()} />
|
||||
</NotificationProvider>
|
||||
</ThemeProvider>
|
||||
const { container } = renderWithProviders(
|
||||
<NotificationProvider>
|
||||
<NotificationPanel anchorEl={null} onClose={vi.fn()} />
|
||||
</NotificationProvider>
|
||||
);
|
||||
|
||||
expect(container.firstChild).toBeNull();
|
||||
|
|
@ -43,12 +43,10 @@ describe('NotificationPanel - Simple Tests', () => {
|
|||
test('should render notification panel with header when anchorEl is provided', () => {
|
||||
const mockAnchorEl = createMockAnchorEl();
|
||||
|
||||
render(
|
||||
<ThemeProvider theme={theme}>
|
||||
<NotificationProvider>
|
||||
<NotificationPanel anchorEl={mockAnchorEl} onClose={vi.fn()} />
|
||||
</NotificationProvider>
|
||||
</ThemeProvider>
|
||||
renderWithProviders(
|
||||
<NotificationProvider>
|
||||
<NotificationPanel anchorEl={mockAnchorEl} onClose={vi.fn()} />
|
||||
</NotificationProvider>
|
||||
);
|
||||
|
||||
expect(screen.getByText('Notifications')).toBeInTheDocument();
|
||||
|
|
@ -57,12 +55,10 @@ describe('NotificationPanel - Simple Tests', () => {
|
|||
test('should show empty state when no notifications', () => {
|
||||
const mockAnchorEl = createMockAnchorEl();
|
||||
|
||||
render(
|
||||
<ThemeProvider theme={theme}>
|
||||
<NotificationProvider>
|
||||
<NotificationPanel anchorEl={mockAnchorEl} onClose={vi.fn()} />
|
||||
</NotificationProvider>
|
||||
</ThemeProvider>
|
||||
renderWithProviders(
|
||||
<NotificationProvider>
|
||||
<NotificationPanel anchorEl={mockAnchorEl} onClose={vi.fn()} />
|
||||
</NotificationProvider>
|
||||
);
|
||||
|
||||
expect(screen.getByText('No notifications')).toBeInTheDocument();
|
||||
|
|
@ -71,12 +67,10 @@ describe('NotificationPanel - Simple Tests', () => {
|
|||
test('should render with theme provider correctly', () => {
|
||||
const mockAnchorEl = createMockAnchorEl();
|
||||
|
||||
const { container } = render(
|
||||
<ThemeProvider theme={theme}>
|
||||
<NotificationProvider>
|
||||
<NotificationPanel anchorEl={mockAnchorEl} onClose={vi.fn()} />
|
||||
</NotificationProvider>
|
||||
</ThemeProvider>
|
||||
const { container } = renderWithProviders(
|
||||
<NotificationProvider>
|
||||
<NotificationPanel anchorEl={mockAnchorEl} onClose={vi.fn()} />
|
||||
</NotificationProvider>
|
||||
);
|
||||
|
||||
// Should render without crashing
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { describe, test, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||
import { render, screen, waitFor } from '@testing-library/react';
|
||||
import { ThemeProvider, createTheme } from '@mui/material/styles';
|
||||
import { screen, waitFor } from '@testing-library/react';
|
||||
import FailedDocumentViewer from '../FailedDocumentViewer';
|
||||
import { api } from '../../services/api';
|
||||
import { renderWithProviders, setupTestEnvironment } from '../../test/test-utils';
|
||||
|
||||
// Mock the API
|
||||
vi.mock('../../services/api', () => ({
|
||||
|
|
@ -11,7 +11,6 @@ vi.mock('../../services/api', () => ({
|
|||
},
|
||||
}));
|
||||
|
||||
const theme = createTheme();
|
||||
|
||||
const defaultProps = {
|
||||
failedDocumentId: 'test-failed-doc-id',
|
||||
|
|
@ -22,10 +21,8 @@ const defaultProps = {
|
|||
const renderFailedDocumentViewer = (props = {}) => {
|
||||
const combinedProps = { ...defaultProps, ...props };
|
||||
|
||||
return render(
|
||||
<ThemeProvider theme={theme}>
|
||||
<FailedDocumentViewer {...combinedProps} />
|
||||
</ThemeProvider>
|
||||
return renderWithProviders(
|
||||
<FailedDocumentViewer {...combinedProps} />
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -44,6 +41,7 @@ global.URL = {
|
|||
|
||||
describe('FailedDocumentViewer', () => {
|
||||
beforeEach(() => {
|
||||
setupTestEnvironment();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,24 +1,21 @@
|
|||
import { describe, test, expect, vi, beforeEach } from 'vitest';
|
||||
import { render, screen, fireEvent } from '@testing-library/react';
|
||||
import { ThemeProvider, createTheme } from '@mui/material/styles';
|
||||
import { screen, fireEvent } from '@testing-library/react';
|
||||
import TestNotification from '../TestNotification';
|
||||
import { NotificationProvider } from '../../contexts/NotificationContext';
|
||||
import { renderWithProviders, setupTestEnvironment } from '../../test/test-utils';
|
||||
import React from 'react';
|
||||
|
||||
const theme = createTheme();
|
||||
|
||||
const renderTestNotification = () => {
|
||||
return render(
|
||||
<ThemeProvider theme={theme}>
|
||||
<NotificationProvider>
|
||||
<TestNotification />
|
||||
</NotificationProvider>
|
||||
</ThemeProvider>
|
||||
return renderWithProviders(
|
||||
<NotificationProvider>
|
||||
<TestNotification />
|
||||
</NotificationProvider>
|
||||
);
|
||||
};
|
||||
|
||||
describe('TestNotification', () => {
|
||||
beforeEach(() => {
|
||||
setupTestEnvironment();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
|
|
@ -126,12 +123,10 @@ describe('TestNotification Integration', () => {
|
|||
);
|
||||
};
|
||||
|
||||
render(
|
||||
<ThemeProvider theme={theme}>
|
||||
<NotificationProvider>
|
||||
<TestWrapper />
|
||||
</NotificationProvider>
|
||||
</ThemeProvider>
|
||||
renderWithProviders(
|
||||
<NotificationProvider>
|
||||
<TestWrapper />
|
||||
</NotificationProvider>
|
||||
);
|
||||
|
||||
expect(screen.getByTestId('notification-count')).toHaveTextContent('0');
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { describe, test, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||
import { render, screen, act } from '@testing-library/react';
|
||||
import { screen, act } from '@testing-library/react';
|
||||
import { NotificationProvider, useNotifications } from '../NotificationContext';
|
||||
import { renderWithProviders, setupTestEnvironment } from '../../test/test-utils';
|
||||
import React from 'react';
|
||||
|
||||
// Simple test component
|
||||
|
|
@ -52,7 +53,7 @@ const SimpleTestComponent: React.FC = () => {
|
|||
};
|
||||
|
||||
const renderWithProvider = () => {
|
||||
return render(
|
||||
return renderWithProviders(
|
||||
<NotificationProvider>
|
||||
<SimpleTestComponent />
|
||||
</NotificationProvider>
|
||||
|
|
@ -61,6 +62,7 @@ const renderWithProvider = () => {
|
|||
|
||||
describe('NotificationContext - Simple Tests', () => {
|
||||
beforeEach(() => {
|
||||
setupTestEnvironment();
|
||||
vi.useFakeTimers();
|
||||
});
|
||||
|
||||
|
|
@ -162,7 +164,7 @@ describe('NotificationContext - Simple Tests', () => {
|
|||
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
||||
|
||||
expect(() => {
|
||||
render(<SimpleTestComponent />);
|
||||
renderWithProviders(<SimpleTestComponent />);
|
||||
}).toThrow('useNotifications must be used within NotificationProvider');
|
||||
|
||||
consoleSpy.mockRestore();
|
||||
|
|
@ -196,6 +198,7 @@ describe('NotificationContext - Types', () => {
|
|||
};
|
||||
|
||||
beforeEach(() => {
|
||||
setupTestEnvironment();
|
||||
vi.useFakeTimers();
|
||||
});
|
||||
|
||||
|
|
@ -204,7 +207,7 @@ describe('NotificationContext - Types', () => {
|
|||
});
|
||||
|
||||
test.each(['success', 'error', 'info', 'warning'] as const)('should handle %s notification type', (type) => {
|
||||
render(
|
||||
renderWithProviders(
|
||||
<NotificationProvider>
|
||||
<TypeTestComponent type={type} />
|
||||
</NotificationProvider>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,166 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Script to migrate remaining tests in documents_tests.rs to use the new TestContext pattern.
|
||||
"""
|
||||
|
||||
import re
|
||||
import sys
|
||||
|
||||
def migrate_test_file(file_path):
|
||||
"""Migrate the documents_tests.rs file to use new test patterns."""
|
||||
|
||||
with open(file_path, 'r') as f:
|
||||
content = f.read()
|
||||
|
||||
# Store the original content for comparison
|
||||
original_content = content
|
||||
|
||||
# 1. Remove #[ignore = "Requires PostgreSQL database"] annotations
|
||||
content = re.sub(r' #\[ignore = "Requires PostgreSQL database"\]\n', '', content)
|
||||
|
||||
# 2. Remove old database pool creation lines
|
||||
content = re.sub(r' let pool = create_test_db_pool\(\)\.await;\n', '', content)
|
||||
|
||||
# 3. Remove old Database struct creation lines
|
||||
content = re.sub(r' let documents_db = Database \{ pool: pool\.clone\(\) \};\n', '', content)
|
||||
|
||||
# 4. Replace old user creation with new pattern
|
||||
# Handle both User and Admin role patterns
|
||||
content = re.sub(
|
||||
r' let user = create_test_user\(&pool, UserRole::User\)\.await;',
|
||||
' let ctx = TestContext::new().await;\n let auth_helper = TestAuthHelper::new(ctx.app.clone());\n let user = auth_helper.create_test_user().await;',
|
||||
content
|
||||
)
|
||||
|
||||
content = re.sub(
|
||||
r' let admin = create_test_user\(&pool, UserRole::Admin\)\.await;',
|
||||
' let admin = auth_helper.create_test_admin().await;',
|
||||
content
|
||||
)
|
||||
|
||||
# Handle other variations of user creation
|
||||
content = re.sub(
|
||||
r' let user1 = create_test_user\(&pool, UserRole::User\)\.await;',
|
||||
' let ctx = TestContext::new().await;\n let auth_helper = TestAuthHelper::new(ctx.app.clone());\n let user1 = auth_helper.create_test_user().await;',
|
||||
content
|
||||
)
|
||||
|
||||
content = re.sub(
|
||||
r' let user2 = create_test_user\(&pool, UserRole::User\)\.await;',
|
||||
' let user2 = auth_helper.create_test_user().await;',
|
||||
content
|
||||
)
|
||||
|
||||
content = re.sub(
|
||||
r' let tenant1_user1 = create_test_user\(&pool, UserRole::User\)\.await;',
|
||||
' let ctx = TestContext::new().await;\n let auth_helper = TestAuthHelper::new(ctx.app.clone());\n let tenant1_user1 = auth_helper.create_test_user().await;',
|
||||
content
|
||||
)
|
||||
|
||||
content = re.sub(
|
||||
r' let tenant1_user2 = create_test_user\(&pool, UserRole::User\)\.await;',
|
||||
' let tenant1_user2 = auth_helper.create_test_user().await;',
|
||||
content
|
||||
)
|
||||
|
||||
content = re.sub(
|
||||
r' let tenant2_user1 = create_test_user\(&pool, UserRole::User\)\.await;',
|
||||
' let tenant2_user1 = auth_helper.create_test_user().await;',
|
||||
content
|
||||
)
|
||||
|
||||
content = re.sub(
|
||||
r' let tenant2_user2 = create_test_user\(&pool, UserRole::User\)\.await;',
|
||||
' let tenant2_user2 = auth_helper.create_test_user().await;',
|
||||
content
|
||||
)
|
||||
|
||||
# 5. Replace document creation and insertion pattern
|
||||
content = re.sub(
|
||||
r' let ([a-zA-Z0-9_]+) = create_and_insert_test_document\(&pool, ([a-zA-Z0-9_.()]+)\)\.await;',
|
||||
r' let \1_doc = create_test_document(\2);\n let \1 = ctx.state.db.create_document(\1_doc).await.expect("Failed to create document");',
|
||||
content
|
||||
)
|
||||
|
||||
# 6. Replace documents_db. with ctx.state.db.
|
||||
content = re.sub(r'documents_db\.', 'ctx.state.db.', content)
|
||||
|
||||
# 7. Replace user.id with user.id() for TestUser instances
|
||||
# This is tricky because we need to be careful about which instances are TestUser vs regular User
|
||||
# We'll handle this pattern by pattern based on context
|
||||
|
||||
# For delete_document calls that use user.id, user.role pattern
|
||||
content = re.sub(
|
||||
r'\.delete_document\(([^,]+), ([a-zA-Z0-9_]+)\.id, ([a-zA-Z0-9_]+)\.role\)',
|
||||
r'.delete_document(\1, \2.id(), \3.role)',
|
||||
content
|
||||
)
|
||||
|
||||
# For bulk_delete_documents calls
|
||||
content = re.sub(
|
||||
r'\.bulk_delete_documents\(([^,]+), ([a-zA-Z0-9_]+)\.id, ([a-zA-Z0-9_]+)\.role\)',
|
||||
r'.bulk_delete_documents(\1, \2.id(), \3.role)',
|
||||
content
|
||||
)
|
||||
|
||||
# For get_document_by_id calls
|
||||
content = re.sub(
|
||||
r'\.get_document_by_id\(([^,]+), ([a-zA-Z0-9_]+)\.id, ([a-zA-Z0-9_]+)\.role\)',
|
||||
r'.get_document_by_id(\1, \2.id(), \3.role)',
|
||||
content
|
||||
)
|
||||
|
||||
# For create_test_document calls
|
||||
content = re.sub(
|
||||
r'create_test_document\(([a-zA-Z0-9_]+)\.id\)',
|
||||
r'create_test_document(\1.id())',
|
||||
content
|
||||
)
|
||||
|
||||
# For bind calls in SQL
|
||||
content = re.sub(
|
||||
r'\.bind\(([a-zA-Z0-9_]+)\.id\)',
|
||||
r'.bind(\1.id())',
|
||||
content
|
||||
)
|
||||
|
||||
# For let user_id assignments
|
||||
content = re.sub(
|
||||
r' let user_id = ([a-zA-Z0-9_]+)\.id;',
|
||||
r' let user_id = \1.id();',
|
||||
content
|
||||
)
|
||||
|
||||
# Add missing imports if TestContext/TestAuthHelper aren't already imported
|
||||
# Check if the imports are present
|
||||
if 'use crate::test_utils::{TestContext, TestAuthHelper};' not in content:
|
||||
# Find the existing test_utils import and update it
|
||||
content = re.sub(
|
||||
r'use crate::test_utils::TestContext;',
|
||||
'use crate::test_utils::{TestContext, TestAuthHelper};',
|
||||
content
|
||||
)
|
||||
|
||||
# Check if we made any changes
|
||||
if content != original_content:
|
||||
return content
|
||||
else:
|
||||
return None
|
||||
|
||||
def main():
|
||||
file_path = '/root/repos/readur/src/tests/documents_tests.rs'
|
||||
|
||||
print("Starting migration of documents_tests.rs...")
|
||||
|
||||
migrated_content = migrate_test_file(file_path)
|
||||
|
||||
if migrated_content:
|
||||
# Write the migrated content back
|
||||
with open(file_path, 'w') as f:
|
||||
f.write(migrated_content)
|
||||
print("Migration completed successfully!")
|
||||
else:
|
||||
print("No changes needed - file is already migrated or no patterns found.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Enhanced script to migrate remaining tests in documents_tests.rs to use the new TestContext pattern.
|
||||
"""
|
||||
|
||||
import re
|
||||
import sys
|
||||
|
||||
def migrate_test_file(file_path):
|
||||
"""Migrate the documents_tests.rs file to use new test patterns."""
|
||||
|
||||
with open(file_path, 'r') as f:
|
||||
content = f.read()
|
||||
|
||||
# Store the original content for comparison
|
||||
original_content = content
|
||||
|
||||
# Fix remaining documents_db references that were missed
|
||||
content = re.sub(r' let result = documents_db', ' let result = ctx.state.db', content)
|
||||
content = re.sub(r' let result2 = documents_db', ' let result2 = ctx.state.db', content)
|
||||
|
||||
# Fix any remaining documents_db references in method calls
|
||||
content = re.sub(r'documents_db\n', 'ctx.state.db\n', content)
|
||||
|
||||
# Fix variable naming from the document creation pattern
|
||||
# The regex replacement created variables like user_doc_doc, let's fix those
|
||||
content = re.sub(r' let ([a-zA-Z0-9_]+)_doc_doc = create_test_document\(([^)]+)\);',
|
||||
r' let \1_doc = create_test_document(\2);', content)
|
||||
|
||||
# Check if we made any changes
|
||||
if content != original_content:
|
||||
return content
|
||||
else:
|
||||
return None
|
||||
|
||||
def main():
|
||||
file_path = '/root/repos/readur/src/tests/documents_tests.rs'
|
||||
|
||||
print("Starting enhanced migration of documents_tests.rs...")
|
||||
|
||||
migrated_content = migrate_test_file(file_path)
|
||||
|
||||
if migrated_content:
|
||||
# Write the migrated content back
|
||||
with open(file_path, 'w') as f:
|
||||
f.write(migrated_content)
|
||||
print("Enhanced migration completed successfully!")
|
||||
else:
|
||||
print("No changes needed - file is already migrated or no patterns found.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Final cleanup script to fix variable naming issues from the migration.
|
||||
"""
|
||||
|
||||
import re
|
||||
import sys
|
||||
|
||||
def migrate_test_file(file_path):
|
||||
"""Clean up variable naming issues from the migration."""
|
||||
|
||||
with open(file_path, 'r') as f:
|
||||
content = f.read()
|
||||
|
||||
# Store the original content for comparison
|
||||
original_content = content
|
||||
|
||||
# Fix the doubled variable names created by the regex
|
||||
content = re.sub(r' let ([a-zA-Z0-9_]+)_doc_doc = create_test_document\(([^)]+)\);',
|
||||
r' let \1_doc = create_test_document(\2);', content)
|
||||
|
||||
# Also fix any references to these variables in the same context
|
||||
content = re.sub(r'create_document\(([a-zA-Z0-9_]+)_doc_doc\)',
|
||||
r'create_document(\1_doc)', content)
|
||||
|
||||
# Check if we made any changes
|
||||
if content != original_content:
|
||||
return content
|
||||
else:
|
||||
return None
|
||||
|
||||
def main():
|
||||
file_path = '/root/repos/readur/src/tests/documents_tests.rs'
|
||||
|
||||
print("Starting final cleanup of documents_tests.rs...")
|
||||
|
||||
migrated_content = migrate_test_file(file_path)
|
||||
|
||||
if migrated_content:
|
||||
# Write the migrated content back
|
||||
with open(file_path, 'w') as f:
|
||||
f.write(migrated_content)
|
||||
print("Final cleanup completed successfully!")
|
||||
else:
|
||||
print("No changes needed - file is already clean.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Bulk migration script to convert old test patterns to new TestContext/TestAuthHelper patterns
|
||||
in documents_tests.rs
|
||||
"""
|
||||
|
||||
import re
|
||||
import sys
|
||||
|
||||
def migrate_test_patterns(content):
|
||||
"""Apply all migration patterns to the content"""
|
||||
|
||||
# Remove #[ignore] attributes
|
||||
content = re.sub(r'\s*#\[ignore = "Requires PostgreSQL database"\]', '', content)
|
||||
|
||||
# Pattern 1: Replace basic test setup
|
||||
# Old pattern:
|
||||
# let pool = create_test_db_pool().await;
|
||||
# let documents_db = Database { pool: pool.clone() };
|
||||
# New pattern:
|
||||
# let ctx = TestContext::new().await;
|
||||
# let auth_helper = TestAuthHelper::new(ctx.app.clone());
|
||||
|
||||
pool_db_pattern = r'let pool = create_test_db_pool\(\)\.await;\s*let documents_db = Database \{ pool: pool\.clone\(\) \};'
|
||||
pool_db_replacement = 'let ctx = TestContext::new().await;\n let auth_helper = TestAuthHelper::new(ctx.app.clone());'
|
||||
content = re.sub(pool_db_pattern, pool_db_replacement, content, flags=re.MULTILINE)
|
||||
|
||||
# Pattern 2: Replace user creation
|
||||
# let user = create_test_user(&pool, UserRole::User).await;
|
||||
# -> let user = auth_helper.create_test_user().await;
|
||||
user_pattern = r'let (\w+) = create_test_user\(&pool, UserRole::User\)\.await;'
|
||||
user_replacement = r'let \1 = auth_helper.create_test_user().await;'
|
||||
content = re.sub(user_pattern, user_replacement, content)
|
||||
|
||||
# Pattern 3: Replace admin creation
|
||||
# let admin = create_test_user(&pool, UserRole::Admin).await;
|
||||
# -> let admin = auth_helper.create_test_admin().await;
|
||||
admin_pattern = r'let (\w+) = create_test_user\(&pool, UserRole::Admin\)\.await;'
|
||||
admin_replacement = r'let \1 = auth_helper.create_test_admin().await;'
|
||||
content = re.sub(admin_pattern, admin_replacement, content)
|
||||
|
||||
# Pattern 4: Replace document creation and insertion
|
||||
# let doc = create_and_insert_test_document(&pool, user.id).await;
|
||||
# -> let doc = create_test_document(user.id());
|
||||
# let doc = ctx.state.db.create_document(doc).await.expect("Failed to create document");
|
||||
doc_pattern = r'let (\w+) = create_and_insert_test_document\(&pool, (\w+)\.id\)\.await;'
|
||||
def doc_replacement(match):
|
||||
doc_name = match.group(1)
|
||||
user_name = match.group(2)
|
||||
return f'let {doc_name} = create_test_document({user_name}.id());\n let {doc_name} = ctx.state.db.create_document({doc_name}).await.expect("Failed to create document");'
|
||||
content = re.sub(doc_pattern, doc_replacement, content)
|
||||
|
||||
# Pattern 5: Replace documents_db. with ctx.state.db.
|
||||
content = re.sub(r'documents_db\.', 'ctx.state.db.', content)
|
||||
|
||||
# Pattern 6: Replace .id with .id() for user objects (be careful with document.id)
|
||||
# Only replace when it's clearly a user/admin object
|
||||
content = re.sub(r'(\w+)\.id(?![().])', r'\1.id()', content)
|
||||
|
||||
# Fix document.id() back to document.id (documents don't have id() method)
|
||||
content = re.sub(r'(doc\w*)\.id\(\)', r'\1.id', content)
|
||||
content = re.sub(r'(document\w*)\.id\(\)', r'\1.id', content)
|
||||
content = re.sub(r'(\w*_doc\w*)\.id\(\)', r'\1.id', content)
|
||||
content = re.sub(r'(result\[\d+\])\.id\(\)', r'\1.id', content)
|
||||
content = re.sub(r'(deleted_doc)\.id\(\)', r'\1.id', content)
|
||||
content = re.sub(r'(found_doc\.unwrap\(\))\.id\(\)', r'\1.id', content)
|
||||
|
||||
return content
|
||||
|
||||
def main():
|
||||
file_path = '/root/repos/readur/src/tests/documents_tests.rs'
|
||||
|
||||
# Read the file
|
||||
try:
|
||||
with open(file_path, 'r') as f:
|
||||
content = f.read()
|
||||
except FileNotFoundError:
|
||||
print(f"Error: Could not find file {file_path}")
|
||||
return 1
|
||||
|
||||
# Apply migrations
|
||||
print("Applying migration patterns...")
|
||||
migrated_content = migrate_test_patterns(content)
|
||||
|
||||
# Write back the migrated content
|
||||
try:
|
||||
with open(file_path, 'w') as f:
|
||||
f.write(migrated_content)
|
||||
print(f"Successfully migrated {file_path}")
|
||||
return 0
|
||||
except Exception as e:
|
||||
print(f"Error writing file: {e}")
|
||||
return 1
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Enhanced migration script to fix remaining issues in documents_tests.rs
|
||||
"""
|
||||
|
||||
import re
|
||||
import sys
|
||||
|
||||
def migrate_remaining_issues(content):
|
||||
"""Fix remaining issues from the bulk migration"""
|
||||
|
||||
# Fix remaining pool references
|
||||
content = re.sub(r'\.execute\(&pool\)', '.execute(&ctx.state.db.pool)', content)
|
||||
|
||||
# Fix Database::new patterns - replace with TestContext
|
||||
database_new_pattern = r'let database = Database::new\(&connection_string\)\.await\.unwrap\(\);'
|
||||
database_new_replacement = 'let ctx = TestContext::new().await;\n let database = &ctx.state.db;'
|
||||
content = re.sub(database_new_pattern, database_new_replacement, content)
|
||||
|
||||
# Also handle the variable name 'database' in subsequent lines
|
||||
# Replace database. with ctx.state.db. only in test functions
|
||||
content = re.sub(r'\bdatabase\.', 'ctx.state.db.', content)
|
||||
|
||||
# Fix cases where we have ctx declared multiple times in the same function
|
||||
# This is a more complex pattern - let's fix it by ensuring we only declare ctx once per function
|
||||
|
||||
# Find functions with multiple ctx declarations and fix them
|
||||
def fix_multiple_ctx(match):
|
||||
func_content = match.group(0)
|
||||
# Count ctx declarations
|
||||
ctx_count = len(re.findall(r'let ctx = TestContext::new\(\)\.await;', func_content))
|
||||
if ctx_count > 1:
|
||||
# Keep only the first one, replace others with comments
|
||||
first_done = False
|
||||
def replace_ctx(ctx_match):
|
||||
nonlocal first_done
|
||||
if not first_done:
|
||||
first_done = True
|
||||
return ctx_match.group(0)
|
||||
else:
|
||||
return '// let ctx = TestContext::new().await; // Already declared above'
|
||||
func_content = re.sub(r'let ctx = TestContext::new\(\)\.await;', replace_ctx, func_content)
|
||||
return func_content
|
||||
|
||||
# Apply this to each test function
|
||||
func_pattern = r'#\[tokio::test\][^}]*?(?=\n #\[tokio::test\]|\n}\n|\Z)'
|
||||
content = re.sub(func_pattern, fix_multiple_ctx, content, flags=re.MULTILINE | re.DOTALL)
|
||||
|
||||
return content
|
||||
|
||||
def main():
|
||||
file_path = '/root/repos/readur/src/tests/documents_tests.rs'
|
||||
|
||||
# Read the file
|
||||
try:
|
||||
with open(file_path, 'r') as f:
|
||||
content = f.read()
|
||||
except FileNotFoundError:
|
||||
print(f"Error: Could not find file {file_path}")
|
||||
return 1
|
||||
|
||||
# Apply additional fixes
|
||||
print("Applying additional migration fixes...")
|
||||
migrated_content = migrate_remaining_issues(content)
|
||||
|
||||
# Write back the migrated content
|
||||
try:
|
||||
with open(file_path, 'w') as f:
|
||||
f.write(migrated_content)
|
||||
print(f"Successfully applied fixes to {file_path}")
|
||||
return 0
|
||||
except Exception as e:
|
||||
print(f"Error writing file: {e}")
|
||||
return 1
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Precise fix for TestUser field access based on variable creation patterns
|
||||
"""
|
||||
|
||||
import re
|
||||
import sys
|
||||
|
||||
def fix_testuser_access(content):
|
||||
"""Fix TestUser objects to use proper .user_response field access"""
|
||||
|
||||
lines = content.split('\n')
|
||||
fixed_lines = []
|
||||
|
||||
# Track which variables are TestUser objects within each function
|
||||
current_testuser_vars = set()
|
||||
in_function = False
|
||||
|
||||
for line in lines:
|
||||
# Reset when entering a new function
|
||||
if re.match(r'\s*#\[tokio::test\]', line) or re.match(r'\s*async fn ', line):
|
||||
current_testuser_vars.clear()
|
||||
in_function = True
|
||||
elif re.match(r'^\s*}$', line) and in_function:
|
||||
in_function = False
|
||||
current_testuser_vars.clear()
|
||||
|
||||
# Track TestUser variable declarations
|
||||
if in_function:
|
||||
# Variables created by auth_helper methods are TestUser
|
||||
testuser_match = re.search(r'let (\w+) = auth_helper\.(?:create_test_user|create_admin_user|create_test_admin)\(\)', line)
|
||||
if testuser_match:
|
||||
var_name = testuser_match.group(1)
|
||||
current_testuser_vars.add(var_name)
|
||||
print(f"Found TestUser variable: {var_name}")
|
||||
|
||||
# Fix field access for known TestUser variables
|
||||
fixed_line = line
|
||||
for var_name in current_testuser_vars:
|
||||
# Replace .id with .user_response.id for TestUser objects
|
||||
fixed_line = re.sub(rf'\b{var_name}\.id\b', f'{var_name}.user_response.id', fixed_line)
|
||||
# Replace .role with .user_response.role for TestUser objects
|
||||
fixed_line = re.sub(rf'\b{var_name}\.role\b', f'{var_name}.user_response.role', fixed_line)
|
||||
|
||||
fixed_lines.append(fixed_line)
|
||||
|
||||
return '\n'.join(fixed_lines)
|
||||
|
||||
def main():
|
||||
file_path = '/root/repos/readur/src/tests/documents_tests.rs'
|
||||
|
||||
# Read the file
|
||||
try:
|
||||
with open(file_path, 'r') as f:
|
||||
content = f.read()
|
||||
except FileNotFoundError:
|
||||
print(f"Error: Could not find file {file_path}")
|
||||
return 1
|
||||
|
||||
# Apply fixes
|
||||
print("Applying precise TestUser field access fixes...")
|
||||
fixed_content = fix_testuser_access(content)
|
||||
|
||||
# Write back the fixed content
|
||||
try:
|
||||
with open(file_path, 'w') as f:
|
||||
f.write(fixed_content)
|
||||
print(f"Successfully fixed {file_path}")
|
||||
return 0
|
||||
except Exception as e:
|
||||
print(f"Error writing file: {e}")
|
||||
return 1
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
|
|
@ -168,6 +168,7 @@ impl TestContext {
|
|||
/// Create a test context with custom configuration
|
||||
pub async fn with_config(config_builder: TestConfigBuilder) -> Self {
|
||||
let postgres_image = Postgres::default()
|
||||
.with_tag("15") // Use PostgreSQL 15 which has gen_random_uuid() built-in
|
||||
.with_env_var("POSTGRES_USER", "test")
|
||||
.with_env_var("POSTGRES_PASSWORD", "test")
|
||||
.with_env_var("POSTGRES_DB", "test");
|
||||
|
|
@ -178,7 +179,10 @@ impl TestContext {
|
|||
let database_url = std::env::var("TEST_DATABASE_URL")
|
||||
.unwrap_or_else(|_| format!("postgresql://test:test@localhost:{}/test", port));
|
||||
let db = crate::db::Database::new(&database_url).await.unwrap();
|
||||
db.migrate().await.unwrap();
|
||||
|
||||
// Run proper SQLx migrations (PostgreSQL 15+ has gen_random_uuid() built-in)
|
||||
let migrations = sqlx::migrate!("./migrations");
|
||||
migrations.run(&db.pool).await.unwrap();
|
||||
|
||||
let config = config_builder.build(database_url);
|
||||
let queue_service = Arc::new(crate::ocr::queue::OcrQueueService::new(db.clone(), db.pool.clone(), 2));
|
||||
|
|
@ -376,6 +380,11 @@ impl TestAuthHelper {
|
|||
}
|
||||
}
|
||||
|
||||
/// Create an admin test user (alias for create_admin_user for backward compatibility)
|
||||
pub async fn create_test_admin(&self) -> TestUser {
|
||||
self.create_admin_user().await
|
||||
}
|
||||
|
||||
/// Login a user and return their authentication token
|
||||
pub async fn login_user(&self, username: &str, password: &str) -> String {
|
||||
let login_data = json!({
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ mod tests {
|
|||
created_at: Utc::now(),
|
||||
updated_at: Utc::now(),
|
||||
user_id,
|
||||
file_hash: Some("abcd1234567890123456789012345678901234567890123456789012345678".to_string()),
|
||||
file_hash: Some(format!("{:x}", Uuid::new_v4().as_u128())), // Generate unique file hash
|
||||
original_created_at: None,
|
||||
original_modified_at: None,
|
||||
source_metadata: None,
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,11 +1,30 @@
|
|||
use sqlx::PgPool;
|
||||
use crate::test_utils::TestContext;
|
||||
use uuid;
|
||||
|
||||
#[cfg(test)]
|
||||
mod migration_constraint_tests {
|
||||
use super::*;
|
||||
|
||||
#[sqlx::test]
|
||||
async fn test_failed_documents_constraint_validation(pool: PgPool) {
|
||||
#[tokio::test]
|
||||
async fn test_failed_documents_constraint_validation() {
|
||||
let ctx = TestContext::new().await;
|
||||
let pool = ctx.state.db.get_pool();
|
||||
|
||||
// Create a test user first to avoid foreign key constraint violations
|
||||
let user_id = uuid::Uuid::new_v4();
|
||||
sqlx::query(
|
||||
"INSERT INTO users (id, username, email, password_hash, role)
|
||||
VALUES ($1, $2, $3, $4, $5)"
|
||||
)
|
||||
.bind(user_id)
|
||||
.bind("test_constraint_user")
|
||||
.bind("test_constraint@example.com")
|
||||
.bind("hash")
|
||||
.bind("user")
|
||||
.execute(pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Test that all allowed failure_reason values work
|
||||
let valid_reasons = vec![
|
||||
"duplicate_content", "duplicate_filename", "unsupported_format",
|
||||
|
|
@ -22,21 +41,40 @@ mod migration_constraint_tests {
|
|||
INSERT INTO failed_documents (
|
||||
user_id, filename, failure_reason, failure_stage, ingestion_source
|
||||
) VALUES (
|
||||
gen_random_uuid(), $1, $2, 'validation', 'test'
|
||||
$1, $2, $3, 'validation', 'test'
|
||||
)
|
||||
"#
|
||||
)
|
||||
.bind(user_id)
|
||||
.bind(format!("test_file_{}.txt", reason))
|
||||
.bind(reason)
|
||||
.execute(&pool)
|
||||
.execute(pool)
|
||||
.await;
|
||||
|
||||
assert!(result.is_ok(), "Valid failure_reason '{}' should be accepted", reason);
|
||||
}
|
||||
}
|
||||
|
||||
#[sqlx::test]
|
||||
async fn test_failed_documents_invalid_constraint_rejection(pool: PgPool) {
|
||||
#[tokio::test]
|
||||
async fn test_failed_documents_invalid_constraint_rejection() {
|
||||
let ctx = TestContext::new().await;
|
||||
let pool = ctx.state.db.get_pool();
|
||||
|
||||
// Create a test user first to avoid foreign key constraint violations
|
||||
let user_id = uuid::Uuid::new_v4();
|
||||
sqlx::query(
|
||||
"INSERT INTO users (id, username, email, password_hash, role)
|
||||
VALUES ($1, $2, $3, $4, $5)"
|
||||
)
|
||||
.bind(user_id)
|
||||
.bind("test_invalid_user")
|
||||
.bind("test_invalid@example.com")
|
||||
.bind("hash")
|
||||
.bind("user")
|
||||
.execute(pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Test that invalid failure_reason values are rejected
|
||||
let invalid_reasons = vec![
|
||||
"invalid_reason", "unknown", "timeout", "memory_limit",
|
||||
|
|
@ -49,21 +87,40 @@ mod migration_constraint_tests {
|
|||
INSERT INTO failed_documents (
|
||||
user_id, filename, failure_reason, failure_stage, ingestion_source
|
||||
) VALUES (
|
||||
gen_random_uuid(), $1, $2, 'validation', 'test'
|
||||
$1, $2, $3, 'validation', 'test'
|
||||
)
|
||||
"#
|
||||
)
|
||||
.bind(user_id)
|
||||
.bind(format!("test_file_{}.txt", reason))
|
||||
.bind(reason)
|
||||
.execute(&pool)
|
||||
.execute(pool)
|
||||
.await;
|
||||
|
||||
assert!(result.is_err(), "Invalid failure_reason '{}' should be rejected", reason);
|
||||
}
|
||||
}
|
||||
|
||||
#[sqlx::test]
|
||||
async fn test_failed_documents_stage_constraint_validation(pool: PgPool) {
|
||||
#[tokio::test]
|
||||
async fn test_failed_documents_stage_constraint_validation() {
|
||||
let ctx = TestContext::new().await;
|
||||
let pool = ctx.state.db.get_pool();
|
||||
|
||||
// Create a test user first to avoid foreign key constraint violations
|
||||
let user_id = uuid::Uuid::new_v4();
|
||||
sqlx::query(
|
||||
"INSERT INTO users (id, username, email, password_hash, role)
|
||||
VALUES ($1, $2, $3, $4, $5)"
|
||||
)
|
||||
.bind(user_id)
|
||||
.bind("test_stage_user")
|
||||
.bind("test_stage@example.com")
|
||||
.bind("hash")
|
||||
.bind("user")
|
||||
.execute(pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Test that all allowed failure_stage values work
|
||||
let valid_stages = vec![
|
||||
"ingestion", "validation", "ocr", "storage", "processing", "sync"
|
||||
|
|
@ -75,21 +132,40 @@ mod migration_constraint_tests {
|
|||
INSERT INTO failed_documents (
|
||||
user_id, filename, failure_reason, failure_stage, ingestion_source
|
||||
) VALUES (
|
||||
gen_random_uuid(), $1, 'other', $2, 'test'
|
||||
$1, $2, 'other', $3, 'test'
|
||||
)
|
||||
"#
|
||||
)
|
||||
.bind(user_id)
|
||||
.bind(format!("test_file_{}.txt", stage))
|
||||
.bind(stage)
|
||||
.execute(&pool)
|
||||
.execute(pool)
|
||||
.await;
|
||||
|
||||
assert!(result.is_ok(), "Valid failure_stage '{}' should be accepted", stage);
|
||||
}
|
||||
}
|
||||
|
||||
#[sqlx::test]
|
||||
async fn test_migration_mapping_compatibility(pool: PgPool) {
|
||||
#[tokio::test]
|
||||
async fn test_migration_mapping_compatibility() {
|
||||
let ctx = TestContext::new().await;
|
||||
let pool = ctx.state.db.get_pool();
|
||||
|
||||
// Create a test user first to avoid foreign key constraint violations
|
||||
let user_id = uuid::Uuid::new_v4();
|
||||
sqlx::query(
|
||||
"INSERT INTO users (id, username, email, password_hash, role)
|
||||
VALUES ($1, $2, $3, $4, $5)"
|
||||
)
|
||||
.bind(user_id)
|
||||
.bind("test_migration_user")
|
||||
.bind("test_migration@example.com")
|
||||
.bind("hash")
|
||||
.bind("user")
|
||||
.execute(pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Test that the migration mapping logic matches our constraints
|
||||
let migration_mappings = vec![
|
||||
("low_ocr_confidence", "low_ocr_confidence"),
|
||||
|
|
@ -127,13 +203,14 @@ mod migration_constraint_tests {
|
|||
INSERT INTO failed_documents (
|
||||
user_id, filename, failure_reason, failure_stage, ingestion_source
|
||||
) VALUES (
|
||||
gen_random_uuid(), $1, $2, 'ocr', 'migration'
|
||||
$1, $2, $3, 'ocr', 'migration'
|
||||
)
|
||||
"#
|
||||
)
|
||||
.bind(user_id)
|
||||
.bind(format!("migration_test_{}.txt", input_reason.replace("/", "_")))
|
||||
.bind(mapped_reason)
|
||||
.execute(&pool)
|
||||
.execute(pool)
|
||||
.await;
|
||||
|
||||
assert!(result.is_ok(),
|
||||
|
|
|
|||
|
|
@ -1,14 +1,29 @@
|
|||
use sqlx::{PgPool, Row};
|
||||
use crate::test_utils::TestContext;
|
||||
use sqlx::Row;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[cfg(test)]
|
||||
mod migration_integration_tests {
|
||||
use super::*;
|
||||
|
||||
#[sqlx::test]
|
||||
async fn test_full_migration_workflow(pool: PgPool) {
|
||||
// Setup: Create sample documents with various OCR failure reasons
|
||||
#[tokio::test]
|
||||
async fn test_full_migration_workflow() {
|
||||
let ctx = TestContext::new().await;
|
||||
let pool = ctx.state.db.get_pool();
|
||||
// Setup: Create a test user first
|
||||
let user_id = Uuid::new_v4();
|
||||
sqlx::query(
|
||||
"INSERT INTO users (id, username, email, password_hash, role)
|
||||
VALUES ($1, $2, $3, $4, $5)"
|
||||
)
|
||||
.bind(user_id)
|
||||
.bind("test_migration_user")
|
||||
.bind("test_migration@example.com")
|
||||
.bind("hash")
|
||||
.bind("user")
|
||||
.execute(pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Create test documents with different failure scenarios
|
||||
let test_documents = vec![
|
||||
|
|
@ -37,7 +52,7 @@ mod migration_integration_tests {
|
|||
.bind(filename)
|
||||
.bind(*failure_reason)
|
||||
.bind(error_msg)
|
||||
.execute(&pool)
|
||||
.execute(pool)
|
||||
.await
|
||||
.expect("Failed to insert test document");
|
||||
}
|
||||
|
|
@ -46,7 +61,7 @@ mod migration_integration_tests {
|
|||
let before_count: i64 = sqlx::query_scalar(
|
||||
"SELECT COUNT(*) FROM documents WHERE ocr_status = 'failed'"
|
||||
)
|
||||
.fetch_one(&pool)
|
||||
.fetch_one(pool)
|
||||
.await
|
||||
.expect("Failed to count documents");
|
||||
|
||||
|
|
@ -57,7 +72,7 @@ mod migration_integration_tests {
|
|||
r#"
|
||||
INSERT INTO failed_documents (
|
||||
user_id, filename, original_filename, file_path, file_size,
|
||||
mime_type, ocr_error, failure_reason, failure_stage, ingestion_source,
|
||||
mime_type, error_message, failure_reason, failure_stage, ingestion_source,
|
||||
created_at, updated_at
|
||||
)
|
||||
SELECT
|
||||
|
|
@ -80,16 +95,19 @@ mod migration_integration_tests {
|
|||
WHERE d.ocr_status = 'failed'
|
||||
"#
|
||||
)
|
||||
.execute(&pool)
|
||||
.execute(pool)
|
||||
.await;
|
||||
|
||||
assert!(migration_result.is_ok(), "Migration should succeed");
|
||||
match migration_result {
|
||||
Ok(_) => {},
|
||||
Err(e) => panic!("Migration failed: {:?}", e),
|
||||
}
|
||||
|
||||
// Verify all documents were migrated
|
||||
let migrated_count: i64 = sqlx::query_scalar(
|
||||
"SELECT COUNT(*) FROM failed_documents WHERE ingestion_source = 'migration'"
|
||||
)
|
||||
.fetch_one(&pool)
|
||||
.fetch_one(pool)
|
||||
.await
|
||||
.expect("Failed to count migrated documents");
|
||||
|
||||
|
|
@ -110,7 +128,7 @@ mod migration_integration_tests {
|
|||
"SELECT failure_reason FROM failed_documents WHERE filename = $1"
|
||||
)
|
||||
.bind(filename)
|
||||
.fetch_one(&pool)
|
||||
.fetch_one(pool)
|
||||
.await
|
||||
.expect("Failed to fetch failure reason");
|
||||
|
||||
|
|
@ -126,7 +144,7 @@ mod migration_integration_tests {
|
|||
let delete_result = sqlx::query(
|
||||
"DELETE FROM documents WHERE ocr_status = 'failed'"
|
||||
)
|
||||
.execute(&pool)
|
||||
.execute(pool)
|
||||
.await;
|
||||
|
||||
assert!(delete_result.is_ok(), "Delete should succeed");
|
||||
|
|
@ -135,7 +153,7 @@ mod migration_integration_tests {
|
|||
let remaining_failed: i64 = sqlx::query_scalar(
|
||||
"SELECT COUNT(*) FROM documents WHERE ocr_status = 'failed'"
|
||||
)
|
||||
.fetch_one(&pool)
|
||||
.fetch_one(pool)
|
||||
.await
|
||||
.expect("Failed to count remaining documents");
|
||||
|
||||
|
|
@ -145,7 +163,7 @@ mod migration_integration_tests {
|
|||
let failed_docs = sqlx::query(
|
||||
"SELECT filename, failure_reason, failure_stage FROM failed_documents ORDER BY filename"
|
||||
)
|
||||
.fetch_all(&pool)
|
||||
.fetch_all(pool)
|
||||
.await
|
||||
.expect("Failed to fetch failed documents");
|
||||
|
||||
|
|
@ -166,10 +184,25 @@ mod migration_integration_tests {
|
|||
}
|
||||
}
|
||||
|
||||
#[sqlx::test]
|
||||
async fn test_migration_with_edge_cases(pool: PgPool) {
|
||||
// Test migration with edge cases that previously caused issues
|
||||
#[tokio::test]
|
||||
async fn test_migration_with_edge_cases() {
|
||||
let ctx = TestContext::new().await;
|
||||
let pool = ctx.state.db.get_pool();
|
||||
|
||||
// Create a test user first
|
||||
let user_id = Uuid::new_v4();
|
||||
sqlx::query(
|
||||
"INSERT INTO users (id, username, email, password_hash, role)
|
||||
VALUES ($1, $2, $3, $4, $5)"
|
||||
)
|
||||
.bind(user_id)
|
||||
.bind("test_edge_user")
|
||||
.bind("test_edge@example.com")
|
||||
.bind("hash")
|
||||
.bind("user")
|
||||
.execute(pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Edge cases that might break migration
|
||||
let edge_cases = vec![
|
||||
|
|
@ -195,7 +228,7 @@ mod migration_integration_tests {
|
|||
.bind(filename)
|
||||
.bind(*failure_reason)
|
||||
.bind(error_msg)
|
||||
.execute(&pool)
|
||||
.execute(pool)
|
||||
.await
|
||||
.expect("Failed to insert edge case document");
|
||||
}
|
||||
|
|
@ -224,7 +257,7 @@ mod migration_integration_tests {
|
|||
WHERE d.ocr_status = 'failed'
|
||||
"#
|
||||
)
|
||||
.execute(&pool)
|
||||
.execute(pool)
|
||||
.await;
|
||||
|
||||
assert!(migration_result.is_ok(), "Migration should handle edge cases");
|
||||
|
|
@ -233,7 +266,7 @@ mod migration_integration_tests {
|
|||
let edge_case_mappings = sqlx::query(
|
||||
"SELECT filename, failure_reason FROM failed_documents WHERE ingestion_source = 'migration_edge_test'"
|
||||
)
|
||||
.fetch_all(&pool)
|
||||
.fetch_all(pool)
|
||||
.await
|
||||
.expect("Failed to fetch edge case mappings");
|
||||
|
||||
|
|
@ -245,10 +278,25 @@ mod migration_integration_tests {
|
|||
}
|
||||
}
|
||||
|
||||
#[sqlx::test]
|
||||
async fn test_constraint_enforcement_during_migration(pool: PgPool) {
|
||||
// This test ensures that if we accidentally introduce invalid data
|
||||
// during migration, the constraints will catch it
|
||||
#[tokio::test]
|
||||
async fn test_constraint_enforcement_during_migration() {
|
||||
let ctx = TestContext::new().await;
|
||||
let pool = ctx.state.db.get_pool();
|
||||
|
||||
// Create a test user first to avoid foreign key constraint violations
|
||||
let user_id = Uuid::new_v4();
|
||||
sqlx::query(
|
||||
"INSERT INTO users (id, username, email, password_hash, role)
|
||||
VALUES ($1, $2, $3, $4, $5)"
|
||||
)
|
||||
.bind(user_id)
|
||||
.bind("test_constraint_user")
|
||||
.bind("test_constraint@example.com")
|
||||
.bind("hash")
|
||||
.bind("user")
|
||||
.execute(pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Try to insert data that violates constraints
|
||||
let invalid_insert = sqlx::query(
|
||||
|
|
@ -256,11 +304,12 @@ mod migration_integration_tests {
|
|||
INSERT INTO failed_documents (
|
||||
user_id, filename, failure_reason, failure_stage, ingestion_source
|
||||
) VALUES (
|
||||
gen_random_uuid(), 'invalid_test.pdf', 'migration_completed', 'migration', 'test'
|
||||
$1, 'invalid_test.pdf', 'migration_completed', 'migration', 'test'
|
||||
)
|
||||
"#
|
||||
)
|
||||
.execute(&pool)
|
||||
.bind(user_id)
|
||||
.execute(pool)
|
||||
.await;
|
||||
|
||||
// This should fail due to constraint violation
|
||||
|
|
|
|||
|
|
@ -6,23 +6,14 @@
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::db::Database;
|
||||
use crate::test_utils::TestContext;
|
||||
use sqlx::Row;
|
||||
use uuid::Uuid;
|
||||
|
||||
async fn create_test_db() -> Database {
|
||||
let db_url = std::env::var("TEST_DATABASE_URL")
|
||||
.unwrap_or_else(|_| "postgresql://postgres:postgres@localhost:5432/readur_test".to_string());
|
||||
|
||||
let db = Database::new(&db_url).await.expect("Failed to connect to test database");
|
||||
db.migrate().await.expect("Failed to migrate test database");
|
||||
db
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_row_trait_import_is_available() {
|
||||
let db = create_test_db().await;
|
||||
let pool = db.get_pool();
|
||||
let ctx = TestContext::new().await;
|
||||
let pool = ctx.state.db.get_pool();
|
||||
|
||||
// This test ensures Row trait is imported and available
|
||||
// The .get() method would fail to compile if Row trait is missing
|
||||
|
|
@ -39,8 +30,8 @@ mod tests {
|
|||
|
||||
#[tokio::test]
|
||||
async fn test_sum_aggregate_type_safety() {
|
||||
let db = create_test_db().await;
|
||||
let pool = db.get_pool();
|
||||
let ctx = TestContext::new().await;
|
||||
let pool = ctx.state.db.get_pool();
|
||||
|
||||
// Create test data
|
||||
let user_id = Uuid::new_v4();
|
||||
|
|
@ -103,8 +94,8 @@ mod tests {
|
|||
|
||||
#[tokio::test]
|
||||
async fn test_group_by_aggregate_type_safety() {
|
||||
let db = create_test_db().await;
|
||||
let pool = db.get_pool();
|
||||
let ctx = TestContext::new().await;
|
||||
let pool = ctx.state.db.get_pool();
|
||||
|
||||
// Test the exact SQL pattern from ignored_files.rs GROUP BY query
|
||||
let results = sqlx::query(
|
||||
|
|
@ -132,8 +123,8 @@ mod tests {
|
|||
|
||||
#[tokio::test]
|
||||
async fn test_numeric_vs_bigint_difference() {
|
||||
let db = create_test_db().await;
|
||||
let pool = db.get_pool();
|
||||
let ctx = TestContext::new().await;
|
||||
let pool = ctx.state.db.get_pool();
|
||||
|
||||
// Demonstrate the difference between NUMERIC and BIGINT return types
|
||||
|
||||
|
|
@ -162,8 +153,8 @@ mod tests {
|
|||
|
||||
#[tokio::test]
|
||||
async fn test_ignored_files_aggregate_queries() {
|
||||
let db = create_test_db().await;
|
||||
let pool = db.get_pool();
|
||||
let ctx = TestContext::new().await;
|
||||
let pool = ctx.state.db.get_pool();
|
||||
|
||||
// Create test user
|
||||
let user_id = Uuid::new_v4();
|
||||
|
|
@ -185,18 +176,20 @@ mod tests {
|
|||
let file_id = Uuid::new_v4();
|
||||
sqlx::query(
|
||||
r#"
|
||||
INSERT INTO ignored_files (id, ignored_by, filename, file_path, file_size, mime_type, source_type, reason)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
||||
INSERT INTO ignored_files (id, ignored_by, filename, original_filename, file_path, file_size, mime_type, source_type, reason, file_hash)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
|
||||
"#
|
||||
)
|
||||
.bind(file_id)
|
||||
.bind(user_id)
|
||||
.bind(format!("ignored_{}.pdf", i))
|
||||
.bind(format!("ignored_{}.pdf", i)) // Add original_filename
|
||||
.bind(format!("/test/ignored_{}.pdf", i))
|
||||
.bind(1024i64 * (i + 1) as i64)
|
||||
.bind("application/pdf")
|
||||
.bind("source_sync")
|
||||
.bind(Some("Test reason"))
|
||||
.bind(format!("{:x}", Uuid::new_v4().as_u128())) // Add unique file_hash
|
||||
.execute(pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
|
@ -255,8 +248,8 @@ mod tests {
|
|||
|
||||
#[tokio::test]
|
||||
async fn test_queue_enqueue_pending_sql_patterns() {
|
||||
let db = create_test_db().await;
|
||||
let pool = db.get_pool();
|
||||
let ctx = TestContext::new().await;
|
||||
let pool = ctx.state.db.get_pool();
|
||||
|
||||
// Test the SQL patterns from queue.rs that need Row trait
|
||||
let pending_documents = sqlx::query(
|
||||
|
|
|
|||
|
|
@ -10,8 +10,19 @@ mod tests {
|
|||
#[tokio::test]
|
||||
async fn test_list_users() {
|
||||
let ctx = TestContext::new().await;
|
||||
let db = &ctx.state.db;
|
||||
|
||||
// Create admin user using direct database approach
|
||||
let admin_data = CreateUser {
|
||||
username: "adminuser".to_string(),
|
||||
email: "admin@example.com".to_string(),
|
||||
password: "adminpass123".to_string(),
|
||||
role: Some(UserRole::Admin),
|
||||
};
|
||||
let admin = db.create_user(admin_data).await.expect("Failed to create admin");
|
||||
|
||||
// Login using TestAuthHelper for token generation
|
||||
let auth_helper = TestAuthHelper::new(ctx.app.clone());
|
||||
let admin = auth_helper.create_admin_user().await;
|
||||
let token = auth_helper.login_user(&admin.username, "adminpass123").await;
|
||||
|
||||
// Create another user
|
||||
|
|
@ -129,12 +140,29 @@ mod tests {
|
|||
#[tokio::test]
|
||||
async fn test_update_user() {
|
||||
let ctx = TestContext::new().await;
|
||||
let db = &ctx.state.db;
|
||||
|
||||
// Create admin user using direct database approach
|
||||
let admin_data = CreateUser {
|
||||
username: "adminuser".to_string(),
|
||||
email: "admin@example.com".to_string(),
|
||||
password: "adminpass123".to_string(),
|
||||
role: Some(UserRole::Admin),
|
||||
};
|
||||
let admin = db.create_user(admin_data).await.expect("Failed to create admin");
|
||||
|
||||
// Login using TestAuthHelper for token generation
|
||||
let auth_helper = TestAuthHelper::new(ctx.app.clone());
|
||||
let admin = auth_helper.create_admin_user().await;
|
||||
let token = auth_helper.login_user(&admin.username, "adminpass123").await;
|
||||
|
||||
// Create a regular user to update
|
||||
let user = auth_helper.create_test_user().await;
|
||||
// Create a regular user using direct database approach
|
||||
let user_data = CreateUser {
|
||||
username: "testuser".to_string(),
|
||||
email: "test@example.com".to_string(),
|
||||
password: "password123".to_string(),
|
||||
role: Some(UserRole::User),
|
||||
};
|
||||
let user = db.create_user(user_data).await.expect("Failed to create user");
|
||||
|
||||
let update_data = UpdateUser {
|
||||
username: Some("updateduser".to_string()),
|
||||
|
|
@ -146,7 +174,7 @@ mod tests {
|
|||
.oneshot(
|
||||
axum::http::Request::builder()
|
||||
.method("PUT")
|
||||
.uri(format!("/api/users/{}", user.id()))
|
||||
.uri(format!("/api/users/{}", user.id))
|
||||
.header("Authorization", format!("Bearer {}", token))
|
||||
.header("Content-Type", "application/json")
|
||||
.body(axum::body::Body::from(serde_json::to_vec(&update_data).unwrap()))
|
||||
|
|
@ -169,12 +197,29 @@ mod tests {
|
|||
#[tokio::test]
|
||||
async fn test_update_user_password() {
|
||||
let ctx = TestContext::new().await;
|
||||
let db = &ctx.state.db;
|
||||
|
||||
// Create admin user using direct database approach
|
||||
let admin_data = CreateUser {
|
||||
username: "adminuser".to_string(),
|
||||
email: "admin@example.com".to_string(),
|
||||
password: "adminpass123".to_string(),
|
||||
role: Some(UserRole::Admin),
|
||||
};
|
||||
let admin = db.create_user(admin_data).await.expect("Failed to create admin");
|
||||
|
||||
// Login using TestAuthHelper for token generation
|
||||
let auth_helper = TestAuthHelper::new(ctx.app.clone());
|
||||
let admin = auth_helper.create_admin_user().await;
|
||||
let token = auth_helper.login_user(&admin.username, "adminpass123").await;
|
||||
|
||||
// Create a regular user to update
|
||||
let user = auth_helper.create_test_user().await;
|
||||
// Create a regular user using direct database approach
|
||||
let user_data = CreateUser {
|
||||
username: "testuser".to_string(),
|
||||
email: "test@example.com".to_string(),
|
||||
password: "password123".to_string(),
|
||||
role: Some(UserRole::User),
|
||||
};
|
||||
let user = db.create_user(user_data).await.expect("Failed to create user");
|
||||
|
||||
let update_data = UpdateUser {
|
||||
username: None,
|
||||
|
|
@ -187,7 +232,7 @@ mod tests {
|
|||
.oneshot(
|
||||
axum::http::Request::builder()
|
||||
.method("PUT")
|
||||
.uri(format!("/api/users/{}", user.id()))
|
||||
.uri(format!("/api/users/{}", user.id))
|
||||
.header("Authorization", format!("Bearer {}", token))
|
||||
.header("Content-Type", "application/json")
|
||||
.body(axum::body::Body::from(serde_json::to_vec(&update_data).unwrap()))
|
||||
|
|
|
|||
Loading…
Reference in New Issue