Instruction file imported from hatefsystems/search-engine-core (
.cursor/rules/testing.mdc). Copyright stays with the author.
Testing Standards and Organization
Test Directory Structure
tests/
├── integration/ # Integration tests (API, end-to-end)
│ ├── README.md
│ ├── test_profile_api.sh
│ ├── test_link_blocks.sh
│ ├── test_website_profile_api.sh
│ └── test_10_concurrent.sh
├── common/ # C++ unit tests for common utilities
├── crawler/ # C++ unit tests for crawler
├── models/ # C++ unit tests for models
├── privacy/ # C++ unit tests for privacy/encryption
├── scoring/ # C++ unit tests for scoring
├── search_core/ # C++ unit tests for search engine core
├── storage/ # C++ unit tests for storage layer
├── text_processing/ # C++ unit tests for text processing
└── webserver/ # C++ unit tests for webserver
scripts/ # Utility scripts (not tests)
Test File Naming
Shell Integration Tests
- Format:
test_<feature_name>.sh - Location:
tests/integration/ - Examples:
test_profile_api.sh- Profile CRUD operationstest_link_blocks.sh- Link blocks and analyticstest_10_concurrent.sh- Concurrent request handling
C++ Unit Tests
- Format:
test_<component>.cpp - Location:
tests/<component>/ - Examples:
tests/storage/test_profile_storage.cpptests/crawler/test_url_parser.cpp
Shell Test Script Standards
Required Structure
#!/bin/bash
set -e # Exit on error
# Color codes for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
# Configuration
API_URL="${API_BASE_URL:-http://localhost:3000}"
echo "Testing Feature Name..."
# Test functions
test_feature_creation() {
echo "Test: Create feature..."
# ... test implementation
echo -e "${GREEN}✅ Feature creation successful${NC}"
}
# Run tests
test_feature_creation
# ... more tests
# Cleanup
cleanup() {
echo "Cleaning up test data..."
# ... cleanup implementation
}
trap cleanup EXIT
echo "All tests passed!"
Required Elements
- Shebang:
#!/bin/bash - Error handling:
set -e(exit on error) - Color codes: For readable output (RED, GREEN, YELLOW, NC)
- Configuration: Support
API_BASE_URLenv var - Test description: Echo test name before running
- Visual feedback: ✅/❌ for pass/fail
- Cleanup: Trap EXIT to clean up test data
- Exit codes: Exit 0 on success, non-zero on failure
JSON Handling
Always use jq for JSON parsing:
# ✅ CORRECT - Use jq for parsing
profile_id=$(echo "$response" | jq -r '.data.id')
success=$(echo "$response" | jq -r '.success')
# ❌ WRONG - Don't use grep/sed for JSON
profile_id=$(echo "$response" | grep -oP '"id": *"\K[^"]+')
API Request Pattern
# Standard POST request
response=$(curl -s -X POST "$API_URL/api/endpoint" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $token" \
-d "$json_data")
# Check for success
if ! echo "$response" | jq -e '.success == true' > /dev/null; then
echo -e "${RED}❌ Request failed${NC}"
echo "$response" | jq .
exit 1
fi
C++ Test Standards
Test Framework
Use Google Test (gtest) for all C++ unit tests:
#include <gtest/gtest.h>
#include "ComponentToTest.h"
TEST(ComponentTest, FeatureWorks) {
// Arrange
Component component;
// Act
auto result = component.doSomething();
// Assert
EXPECT_TRUE(result.isSuccess());
EXPECT_EQ(result.value, "expected");
}
Test Organization
// Group related tests in test suites
class ProfileStorageTest : public ::testing::Test {
protected:
void SetUp() override {
// Setup before each test
}
void TearDown() override {
// Cleanup after each test
}
ProfileStorage storage;
};
TEST_F(ProfileStorageTest, CreateProfile) {
// ... test implementation
}
TEST_F(ProfileStorageTest, FindById) {
// ... test implementation
}
Integration Test Checklist
When writing integration tests:
- Uses
tests/integration/directory - Named
test_<feature>.sh - Includes error handling (
set -e) - Has colored output (✅/❌)
- Uses
jqfor JSON parsing - Cleans up test data (trap EXIT)
- Tests both success and failure cases
- Validates response structure
- Tests authorization/authentication
- Documents test in
tests/integration/README.md
Running Tests
Integration Tests
# Run single test
./tests/integration/test_profile_api.sh
# Run all integration tests
for test in tests/integration/test_*.sh; do
bash "$test" || exit 1
done
C++ Unit Tests
# Build and run all tests
cd build
cmake .. -DBUILD_TESTS=ON
make
ctest --output-on-failure
# Run specific test
./tests/storage/test_profile_storage
Test Data Management
Test Data Generation
# Generate random test data
random_slug="test-$(date +%s)-$RANDOM"
random_email="test-$RANDOM@example.com"
# Use timestamp for uniqueness
timestamp=$(date +%s)
test_id="test_$timestamp"
Cleanup Pattern
cleanup() {
echo "Cleaning up..."
# Delete created resources
if [ -n "$profile_id" ]; then
curl -s -X DELETE "$API_URL/api/profiles/$profile_id" \
-H "Authorization: Bearer $owner_token" > /dev/null
fi
# Log cleanup status
echo "Cleanup complete"
}
# Register cleanup on exit
trap cleanup EXIT
Performance Testing
Load Testing Scripts
Place load/performance tests in tests/performance/:
tests/performance/
├── load_test_profiles.sh
├── load_test_search.sh
└── benchmark_redirects.sh
Benchmark Format
#!/bin/bash
# Measure operation timing
start=$(date +%s%N)
# ... operation ...
end=$(date +%s%N)
duration=$(( (end - start) / 1000000 )) # Convert to ms
echo "Operation took ${duration}ms"
CI/CD Integration
GitHub Actions Example
name: Integration Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Start services
run: docker-compose up -d
- name: Wait for services
run: sleep 10
- name: Run integration tests
run: |
for test in tests/integration/test_*.sh; do
bash "$test" || exit 1
done
- name: Stop services
run: docker-compose down
Test Coverage Goals
- Unit tests: 80%+ code coverage for critical components
- Integration tests: All API endpoints covered
- Performance tests: All critical paths benchmarked
- Security tests: All auth/authorization flows validated
Debugging Tests
Enable Verbose Mode
# In test script
if [ "$TEST_VERBOSE" = "1" ]; then
set -x # Print commands as they execute
fi
# Run with verbose
TEST_VERBOSE=1 ./tests/integration/test_profile_api.sh
Inspect Responses
# Pretty-print JSON responses
echo "$response" | jq .
# Save response to file
echo "$response" | jq . > /tmp/test_response.json
Common Patterns
Retry Logic
retry_request() {
local url=$1
local max_attempts=3
local attempt=1
while [ $attempt -le $max_attempts ]; do
response=$(curl -s "$url")
if [ $? -eq 0 ]; then
echo "$response"
return 0
fi
attempt=$((attempt + 1))
sleep 1
done
return 1
}
Wait for Service
wait_for_service() {
local url=$1
local timeout=30
local elapsed=0
while [ $elapsed -lt $timeout ]; do
if curl -s "$url/health" > /dev/null 2>&1; then
return 0
fi
sleep 1
elapsed=$((elapsed + 1))
done
return 1
}
Best Practices
- Isolation: Each test should be independent (no shared state)
- Idempotency: Tests should be repeatable without side effects
- Speed: Integration tests should complete in < 60s
- Clarity: Use descriptive test names and error messages
- Cleanup: Always clean up test data, even on failure
- Documentation: Update
tests/integration/README.mdfor new tests - Environment: Don't hardcode URLs/credentials, use env vars
- Validation: Test both success paths and error handling
References
- Integration Tests README:
tests/integration/README.md - API Documentation:
docs/api/ - Quick Start Guides:
docs/api/*_QUICK_START.md