Skip to content

Commit f7c55df

Browse files
committed
Add comprehensive test suite for logseq_to_obsidian.py
- Create comprehensive test suite covering all script functionality - Test task status conversion (TODO, DOING, DONE, etc.) - Test frontmatter generation and YAML parsing - Test journal file renaming from underscore to hyphen format - Test property stripping functionality - Test block reference conversion behavior - Test tag conversion (#[[Tag With Spaces]] → #tag-with-spaces) - Test date link conversion ([[YYYY_MM_DD]] → [[YYYY-MM-DD]]) - Test scheduled/deadline property handling - Test error handling with malformed files - Test dry-run mode behavior - Test command-line interface and help output - Test integration with example files - Add test runner script with detailed reporting - Add test configuration and documentation - All 16 tests passing ✅ The test suite validates that the script does exactly what it claims to do in the README and documentation, ensuring reliability and correctness.
1 parent acb143a commit f7c55df

File tree

5 files changed

+839
-0
lines changed

5 files changed

+839
-0
lines changed

tests/README.md

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
# Testing Guide
2+
3+
This directory contains comprehensive tests for the Logseq to Obsidian migration tool.
4+
5+
## Test Structure
6+
7+
### Core Tests (`test_logseq_to_obsidian.py`)
8+
Tests all the core functionality of the migration script:
9+
10+
- **Task Status Conversion**: Tests conversion of TODO, DOING, DONE, etc. to Obsidian format
11+
- **Frontmatter Generation**: Tests YAML frontmatter creation and parsing
12+
- **Journal File Renaming**: Tests conversion from `YYYY_MM_DD.md` to `YYYY-MM-DD.md`
13+
- **Property Stripping**: Tests removal of Logseq-specific properties
14+
- **Block Reference Conversion**: Tests conversion of `((uuid))` references
15+
- **Tag Conversion**: Tests conversion of `#[[Tag With Spaces]]` to `#tag-with-spaces`
16+
- **Date Link Conversion**: Tests conversion of `[[YYYY_MM_DD]]` to `[[YYYY-MM-DD]]`
17+
- **Scheduled/Deadline Conversion**: Tests conversion of scheduled and deadline properties
18+
- **Error Handling**: Tests graceful handling of malformed files
19+
- **Dry Run Mode**: Tests that dry-run mode doesn't create files
20+
- **Command Line Interface**: Tests help output and argument parsing
21+
22+
### Integration Tests (`test_examples.py`)
23+
Tests the script with the provided example files:
24+
25+
- **Dry Run with Examples**: Tests dry-run mode with sample files
26+
- **Full Migration with Examples**: Tests complete migration with all options
27+
- **Minimal Migration**: Tests basic migration without conversion options
28+
29+
## Running Tests
30+
31+
### Quick Test Run
32+
```bash
33+
# Run all tests
34+
python3 tests/run_tests.py
35+
36+
# Run specific test file
37+
python3 -m unittest tests.test_logseq_to_obsidian -v
38+
39+
# Run specific test class
40+
python3 -m unittest tests.test_logseq_to_obsidian.TestLogseqToObsidian.test_task_status_conversion -v
41+
```
42+
43+
### Manual Testing
44+
```bash
45+
# Test with example files
46+
python3 logseq_to_obsidian.py --src examples/sample_logseq --out test_output --frontmatter --status-tags --strip-properties --rename-journals --dry-run
47+
48+
# Test actual migration
49+
python3 logseq_to_obsidian.py --src examples/sample_logseq --out test_output --frontmatter --status-tags --strip-properties --rename-journals
50+
```
51+
52+
## Test Coverage
53+
54+
The tests cover all major functionality claimed by the script:
55+
56+
### ✅ Task Status Conversion
57+
- TODO → `[ ]` with `#status/todo`
58+
- DOING → `[ ]` with `#status/doing`
59+
- NOW → `[ ]` with `#status/now`
60+
- LATER → `[ ]` with `#status/later`
61+
- WAITING → `[ ]` with `#status/waiting`
62+
- DONE → `[x]` with `#status/done`
63+
- CANCELED/CANCELLED → `[x]` with `#status/done`
64+
65+
### ✅ Frontmatter Features
66+
- YAML frontmatter generation
67+
- Title extraction from filename
68+
- Date extraction from filename or properties
69+
- Tag conversion from properties
70+
- Error handling for malformed YAML
71+
72+
### ✅ File Operations
73+
- Journal file renaming (underscore to hyphen)
74+
- Property stripping
75+
- Block reference conversion
76+
- Tag format conversion
77+
- Date link conversion
78+
79+
### ✅ Error Handling
80+
- Malformed YAML frontmatter
81+
- Missing source directories
82+
- Empty directories
83+
- Invalid command line arguments
84+
85+
### ✅ Command Line Interface
86+
- Help output
87+
- All command line options
88+
- Dry-run mode
89+
- Error reporting
90+
91+
## Test Data
92+
93+
### Sample Files
94+
- `examples/sample_logseq/pages/Sample Page.md` - Contains various task statuses, properties, and links
95+
- `examples/sample_logseq/journals/2023_09_04.md` - Sample journal entry
96+
97+
### Generated Test Files
98+
Tests create temporary files with specific content to test individual features.
99+
100+
## Continuous Integration
101+
102+
The GitHub Actions workflow (`.github/workflows/test.yml`) automatically runs these tests on:
103+
- Every push to main branch
104+
- Every pull request
105+
- Multiple Python versions (3.8, 3.9, 3.10, 3.11, 3.12)
106+
107+
## Adding New Tests
108+
109+
When adding new features to the script:
110+
111+
1. **Add test case** to `test_logseq_to_obsidian.py`
112+
2. **Test the feature** with various inputs
113+
3. **Test error cases** and edge conditions
114+
4. **Update documentation** if needed
115+
5. **Run tests** to ensure they pass
116+
117+
### Test Case Template
118+
```python
119+
def test_new_feature(self):
120+
"""Test description of what this feature does"""
121+
# Setup test data
122+
content = """# Test Content
123+
124+
Test content here.
125+
"""
126+
self.create_test_file('pages/Test Feature.md', content)
127+
128+
# Run migration
129+
result = self.run_script([
130+
'--src', self.test_dir,
131+
'--out', os.path.join(self.test_dir, 'output'),
132+
'--new-option'
133+
])
134+
135+
# Verify results
136+
self.assertEqual(result.returncode, 0)
137+
138+
# Check output
139+
output_file = os.path.join(self.test_dir, 'output', 'pages', 'Test Feature.md')
140+
self.assertTrue(os.path.exists(output_file))
141+
142+
with open(output_file, 'r', encoding='utf-8') as f:
143+
output_content = f.read()
144+
145+
# Verify specific conversions
146+
self.assertIn('expected output', output_content)
147+
self.assertNotIn('unexpected content', output_content)
148+
```
149+
150+
## Troubleshooting Tests
151+
152+
### Common Issues
153+
154+
1. **Import Errors**: Make sure the script path is correct
155+
2. **Permission Errors**: Ensure test directories are writable
156+
3. **Path Issues**: Use absolute paths for test files
157+
4. **Encoding Issues**: Always specify UTF-8 encoding
158+
159+
### Debug Mode
160+
```bash
161+
# Run with verbose output
162+
python3 -m unittest tests.test_logseq_to_obsidian -v
163+
164+
# Run single test with debug output
165+
python3 -c "
166+
import sys
167+
sys.path.insert(0, 'tests')
168+
from test_logseq_to_obsidian import TestLogseqToObsidian
169+
import unittest
170+
suite = unittest.TestLoader().loadTestsFromName('test_logseq_to_obsidian.TestLogseqToObsidian.test_task_status_conversion')
171+
runner = unittest.TextTestRunner(verbosity=2)
172+
runner.run(suite)
173+
"
174+
```
175+
176+
## Test Results
177+
178+
A successful test run should show:
179+
- All tests passing ✅
180+
- No failures or errors
181+
- Proper file conversions
182+
- Correct output format
183+
- Error handling working as expected
184+
185+
The tests validate that the script does exactly what it claims to do in the README and documentation.

tests/run_tests.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Test runner for logseq_to_obsidian.py
4+
5+
This script runs all tests and provides a summary of results.
6+
"""
7+
8+
import sys
9+
import os
10+
import subprocess
11+
import unittest
12+
13+
def run_tests():
14+
"""Run all tests and return results"""
15+
# Add tests directory to path
16+
tests_dir = os.path.dirname(os.path.abspath(__file__))
17+
sys.path.insert(0, tests_dir)
18+
19+
# Discover and run tests
20+
loader = unittest.TestLoader()
21+
suite = loader.discover(tests_dir, pattern='test_*.py')
22+
23+
runner = unittest.TextTestRunner(verbosity=2)
24+
result = runner.run(suite)
25+
26+
return result
27+
28+
def main():
29+
"""Main test runner"""
30+
print("🧪 Running Logseq to Obsidian Migration Tool Tests")
31+
print("=" * 60)
32+
33+
# Run tests
34+
result = run_tests()
35+
36+
# Print summary
37+
print("\n" + "=" * 60)
38+
print("📊 Test Summary")
39+
print("=" * 60)
40+
41+
total_tests = result.testsRun
42+
failures = len(result.failures)
43+
errors = len(result.errors)
44+
skipped = len(result.skipped) if hasattr(result, 'skipped') else 0
45+
passed = total_tests - failures - errors - skipped
46+
47+
print(f"Total Tests: {total_tests}")
48+
print(f"✅ Passed: {passed}")
49+
print(f"❌ Failed: {failures}")
50+
print(f"💥 Errors: {errors}")
51+
print(f"⏭️ Skipped: {skipped}")
52+
53+
if failures > 0:
54+
print(f"\n❌ Failures:")
55+
for test, traceback in result.failures:
56+
print(f" - {test}: {traceback.split('AssertionError: ')[-1].split('\\n')[0]}")
57+
58+
if errors > 0:
59+
print(f"\n💥 Errors:")
60+
for test, traceback in result.errors:
61+
print(f" - {test}: {traceback.split('\\n')[-2]}")
62+
63+
# Return appropriate exit code
64+
if failures > 0 or errors > 0:
65+
print(f"\n❌ Tests failed!")
66+
return 1
67+
else:
68+
print(f"\n✅ All tests passed!")
69+
return 0
70+
71+
if __name__ == '__main__':
72+
sys.exit(main())

tests/test_config.ini

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Test configuration and utilities
2+
3+
[test]
4+
# Test settings
5+
verbose = true
6+
failfast = false
7+
buffer = true
8+
9+
[coverage]
10+
# Coverage settings (if using coverage.py)
11+
source = .
12+
omit =
13+
tests/*
14+
examples/*
15+
.git/*
16+
__pycache__/*
17+
18+
[flake8]
19+
# Code style settings
20+
max-line-length = 120
21+
ignore = E501,W503
22+
exclude =
23+
.git,
24+
__pycache__,
25+
.venv,
26+
logseq_env

0 commit comments

Comments
 (0)