blob: 4f1aedfa9c65d334274d623f68b1893dd092b3bb [file] [view]
# TSFile Viewer Testing Guide
This document describes the testing strategy and how to run tests for the TSFile Viewer application.
## Overview
The project includes comprehensive tests for both backend and frontend:
- **Backend**: 131 JUnit 5 tests covering utilities, services, and controllers
- **Frontend**: Vitest tests for components, stores, and API client
## Backend Testing
### Test Coverage
The backend has 131 passing tests covering:
1. **TSFile Utilities** (`tsfile/` package)
- TsFileParser: Metadata extraction, measurement parsing, RowGroup/Chunk parsing
- TsFileDataReader: Data reading with filters, pagination, time range queries
- TsFileReaderCache: Cache behavior, LRU eviction, concurrent access
2. **Service Layer** (`service/` package)
- FileService: File tree browsing, upload validation, path validation
- MetadataService: Metadata caching, parsing, retrieval
- DataService: Data filtering, aggregation, downsampling (LTTB algorithm)
3. **Controller Layer** (`controller/` package)
- FileController: File tree endpoint, upload endpoint
- MetadataController: Metadata retrieval endpoint
- DataController: Data preview and chart query endpoints
4. **Error Handling**
- GlobalExceptionHandler: Error response formatting
- Validation errors, file not found, timeout handling
- HTTP status code correctness
### Running Backend Tests
```bash
cd backend
# Run all tests
mvn test
# Run specific test class
mvn test -Dtest=TsFileReaderCacheTest
# Run tests with coverage
mvn test jacoco:report
# Skip tests during build
mvn clean package -DskipTests
```
### Test Configuration
Tests use:
- **JUnit 5**: Test framework
- **Mockito**: Mocking framework
- **AssertJ**: Fluent assertions
- **Spring Boot Test**: Integration testing support
- **MockMvc**: Controller testing
### Example Test
```java
@SpringBootTest
@AutoConfigureMockMvc
class MetadataControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
void getMetadata_returnsMetadata_whenFileExists() throws Exception {
mockMvc.perform(get("/api/meta/test-123"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.fileId").value("test-123"))
.andExpect(jsonPath("$.version").exists());
}
}
```
## Frontend Testing
### Test Coverage
The frontend includes tests for:
1. **Components** (`components/__tests__/`)
- FilterPanel: Filter controls, event emissions, validation
- DataTable: Data rendering, loading states, error states, pagination
- ChartPanel: Chart rendering, loading states, point-click events
2. **Stores** (`stores/__tests__/`)
- useFileStore: Current file state, recent files management
- useMetaStore: Metadata caching, loading states, error handling
3. **API Client** (`api/__tests__/`)
- Request configuration, retry logic, error handling
### Running Frontend Tests
```bash
cd frontend
# Install dependencies first
pnpm install
# Run all tests
pnpm test:unit
# Run tests in watch mode
pnpm test:unit --watch
# Run tests with coverage
pnpm test:unit --coverage
# Run specific test file
pnpm test:unit FilterPanel.spec.ts
```
### Test Configuration
Tests use:
- **Vitest**: Fast unit test framework
- **Vue Test Utils**: Vue component testing utilities
- **@testing-library/vue**: Additional testing utilities
### Example Test
```typescript
import { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils'
import FilterPanel from '../FilterPanel.vue'
describe('FilterPanel', () => {
it('emits filter-change event when filters are updated', async () => {
const wrapper = mount(FilterPanel, {
props: { fileId: 'test-123' }
})
await wrapper.vm.$emit('change', {
startTime: 1000000,
endTime: 2000000,
limit: 100,
offset: 0,
})
expect(wrapper.emitted('change')).toBeTruthy()
})
})
```
## Integration Testing
### Backend Integration Tests
Integration tests verify end-to-end functionality:
```java
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class FileUploadIntegrationTest {
@Autowired
private TestRestTemplate restTemplate;
@Test
void uploadFile_returnsFileId() {
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
body.add("file", new FileSystemResource("test.tsfile"));
ResponseEntity<UploadResponse> response = restTemplate.postForEntity(
"/api/files/upload",
body,
UploadResponse.class
);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(response.getBody().getFileId()).isNotNull();
}
}
```
### Frontend Integration Tests
Component integration tests verify interactions:
```typescript
describe('Data Preview Workflow', () => {
it('loads and displays data with filters', async () => {
const wrapper = mount(DataPreviewView, {
props: { fileId: 'test-123' }
})
// Apply filters
await wrapper.findComponent(FilterPanel).vm.$emit('change', {
startTime: 1000000,
endTime: 2000000,
})
// Wait for data to load
await wrapper.vm.$nextTick()
// Verify data is displayed
expect(wrapper.findComponent(DataTable).props('data')).toHaveLength(100)
})
})
```
## Test Data
### Backend Test Data
Test TSFiles are located in `backend/src/test/resources/`:
- `test-tree-model.tsfile`: Sample Tree Model TSFile
- `test-table-model.tsfile`: Sample Table Model TSFile
### Frontend Test Data
Mock data is defined in test files:
```typescript
const mockData: DataRow[] = [
{
timestamp: 1000000,
device: 'device1',
measurements: {
temperature: 25.5,
humidity: 60,
},
},
]
```
## Continuous Integration
### GitHub Actions (Example)
```yaml
name: Tests
on: [push, pull_request]
jobs:
backend:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-java@v3
with:
java-version: '21'
- run: cd backend && mvn test
frontend:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: pnpm/action-setup@v2
- uses: actions/setup-node@v3
with:
node-version: '22'
- run: cd frontend && pnpm install && pnpm test:unit
```
## Test Best Practices
### Backend
1. **Use Descriptive Test Names**: `getMetadata_returnsMetadata_whenFileExists`
2. **Follow AAA Pattern**: Arrange, Act, Assert
3. **Mock External Dependencies**: Use Mockito for file system, network calls
4. **Test Edge Cases**: Empty data, null values, boundary conditions
5. **Verify Error Handling**: Test exception scenarios
### Frontend
1. **Test User Interactions**: Clicks, inputs, form submissions
2. **Test Component Props**: Verify prop validation and defaults
3. **Test Event Emissions**: Verify components emit correct events
4. **Test Loading States**: Verify loading indicators appear/disappear
5. **Test Error States**: Verify error messages display correctly
## Code Coverage
### Backend Coverage Goals
- **Line Coverage**: > 80%
- **Branch Coverage**: > 70%
- **Method Coverage**: > 85%
Generate coverage report:
```bash
mvn test jacoco:report
# Report: backend/target/site/jacoco/index.html
```
### Frontend Coverage Goals
- **Statements**: > 75%
- **Branches**: > 70%
- **Functions**: > 75%
- **Lines**: > 75%
Generate coverage report:
```bash
pnpm test:unit --coverage
# Report: frontend/coverage/index.html
```
## Performance Testing
### Backend Performance Tests
Test query performance with large datasets:
```java
@Test
void queryLargeDataset_completesWithinTimeout() {
long startTime = System.currentTimeMillis();
DataPreviewRequest request = new DataPreviewRequest();
request.setFileId("large-file");
request.setLimit(1000);
dataService.previewData(request);
long duration = System.currentTimeMillis() - startTime;
assertThat(duration).isLessThan(5000); // 5 seconds
}
```
### Frontend Performance Tests
Test rendering performance:
```typescript
it('renders large dataset efficiently', async () => {
const largeDataset = Array.from({ length: 10000 }, (_, i) => ({
timestamp: i * 1000,
device: `device${i}`,
measurements: { value: Math.random() * 100 },
}))
const startTime = performance.now()
const wrapper = mount(DataTable, {
props: { data: largeDataset }
})
const renderTime = performance.now() - startTime
expect(renderTime).toBeLessThan(1000) // 1 second
})
```
## Debugging Tests
### Backend
```bash
# Run tests in debug mode
mvn test -Dmaven.surefire.debug
# Connect debugger to port 5005
```
### Frontend
```bash
# Run tests with debugger
pnpm test:unit --inspect-brk
# Open chrome://inspect in Chrome
```
## Troubleshooting
### Backend Tests Fail
1. **Check Java Version**: Ensure JDK 17 or 21 is installed
2. **Clean Build**: Run `mvn clean` before testing
3. **Check Dependencies**: Run `mvn dependency:tree`
4. **Review Logs**: Check `target/surefire-reports/`
### Frontend Tests Fail
1. **Reinstall Dependencies**: `rm -rf node_modules && pnpm install`
2. **Clear Cache**: `pnpm store prune`
3. **Check Node Version**: Ensure Node.js 20.19+ or 22.12+
4. **Review Console**: Check browser console for errors
## Test Maintenance
### Adding New Tests
1. **Backend**: Create test class in `src/test/java/` matching package structure
2. **Frontend**: Create `.spec.ts` file in `__tests__/` directory next to component
### Updating Tests
When modifying code:
1. Update corresponding tests
2. Add tests for new functionality
3. Remove tests for deleted functionality
4. Verify all tests pass before committing
### Test Review Checklist
- [ ] Tests are descriptive and clear
- [ ] Tests cover happy path and edge cases
- [ ] Tests are independent and isolated
- [ ] Tests run quickly (< 1 second each)
- [ ] Tests are deterministic (no flaky tests)
- [ ] Tests follow project conventions
## Resources
- [JUnit 5 Documentation](https://junit.org/junit5/docs/current/user-guide/)
- [Mockito Documentation](https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html)
- [Vitest Documentation](https://vitest.dev/)
- [Vue Test Utils](https://test-utils.vuejs.org/)
- [Spring Boot Testing](https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.testing)
## Summary
The TSFile Viewer project has comprehensive test coverage:
- **Backend**: 131 passing tests covering all layers
- **Frontend**: Component, store, and API client tests
- **Integration**: End-to-end workflow tests
Run tests regularly during development to catch issues early and maintain code quality.