Compare commits

..

3 Commits

Author SHA1 Message Date
phernandez d8c13bf1d3 fix project table unique constraint bug
Signed-off-by: phernandez <paul@basicmachines.co>
2025-06-03 14:23:15 -05:00
phernandez 3f70f5ed42 feat: add /project:test-live command for comprehensive real-world testing
Implements live testing suite that:
- Uses installed Basic Memory version via MCP
- Follows TESTING.md methodology systematically
- Records all observations in Basic Memory notes
- Tests all 5 phases: core, features, edge cases, workflows, stress
- Creates dedicated test project for isolation
- Documents bugs with reproduction steps
- Tracks performance metrics and UX insights
- Validates v0.13.0 features in real usage scenarios

This enables 'Basic Memory testing itself' - comprehensive integration
testing that creates living documentation of test results.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-03 09:40:52 -05:00
phernandez 569a3de80b feat: add comprehensive custom Claude Code slash commands
Adds custom slash commands for streamlined development workflow:

Release Management (/project:release:*):
- beta - Create beta releases with automated quality checks
- release - Create stable releases with comprehensive validation
- release-check - Pre-flight validation without making changes
- changelog - Generate changelog entries from commits

Development (/project:*):
- test-coverage - Run tests with detailed coverage analysis
- lint-fix - Comprehensive code quality fixes with auto-repair
- check-health - Project health assessment and metrics

Commands are organized in .claude/commands/ directory following Claude Code
conventions and provide structured automation for common development tasks.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-03 09:24:03 -05:00
14 changed files with 1702 additions and 19 deletions
+190
View File
@@ -0,0 +1,190 @@
# /project:check-health - Project Health Assessment
Comprehensive health check of the Basic Memory project including code quality, test coverage, dependencies, and documentation.
## Usage
```
/project:check-health
```
## Implementation
You are an expert DevOps engineer for the Basic Memory project. When the user runs `/project:check-health`, execute the following comprehensive assessment:
### Step 1: Git Repository Health
1. **Repository Status**
```bash
git status
git log --oneline -5
git branch -vv
```
- Check working directory status
- Verify branch alignment with remote
- Check recent commit activity
2. **Branch Analysis**
- Verify on main branch
- Check if ahead/behind remote
- Identify any untracked files
### Step 2: Code Quality Assessment
1. **Linting and Formatting**
```bash
uv run ruff check .
uv run pyright
```
- Count linting issues by severity
- Check type annotation coverage
- Verify code formatting compliance
2. **Test Suite Health**
```bash
uv run pytest --collect-only -q
uv run pytest --co -q | wc -l
```
- Count total tests
- Check for test discovery issues
- Verify test structure integrity
### Step 3: Dependency Analysis
1. **Dependency Health**
```bash
uv tree
uv lock --dry-run
```
- Check for dependency conflicts
- Identify outdated dependencies
- Verify lock file consistency
2. **Security Scan**
```bash
uv run pip-audit --desc
```
- Scan for known vulnerabilities
- Check dependency licenses
- Identify security advisories
### Step 4: Performance Metrics
1. **Test Performance**
```bash
uv run pytest --durations=10
```
- Identify slowest tests
- Check overall test execution time
- Monitor test suite growth
2. **Build Performance**
```bash
time uv run python -c "import basic_memory"
```
- Check import time
- Validate package installation
- Monitor startup performance
### Step 5: Documentation Health
1. **Documentation Coverage**
- Check README.md currency
- Verify CLI documentation
- Validate MCP tool documentation
- Check changelog completeness
2. **API Documentation**
- Verify docstring coverage
- Check type annotation completeness
- Validate example code
### Step 6: Project Metrics
1. **Code Statistics**
```bash
find src -name "*.py" | xargs wc -l
find tests -name "*.py" | xargs wc -l
```
- Lines of code trends
- Test-to-code ratio
- File organization metrics
## Health Report Format
Generate comprehensive health dashboard:
```
🏥 Basic Memory Project Health Report
📊 OVERALL HEALTH: 🟢 EXCELLENT (92/100)
🗂️ GIT REPOSITORY
✅ Clean working directory
✅ Up to date with origin/main
✅ Recent commit activity (5 commits this week)
🔍 CODE QUALITY
✅ Linting: 0 errors, 2 warnings
✅ Type checking: 100% coverage
✅ Formatting: Compliant
⚠️ Complex functions: 3 need refactoring
🧪 TEST SUITE
✅ Total tests: 744
✅ Test discovery: All tests found
✅ Coverage: 98.2%
⚡ Performance: 45.2s (good)
📦 DEPENDENCIES
✅ Dependencies: Up to date
✅ Security: No vulnerabilities
✅ Conflicts: None detected
⚠️ Outdated: 2 minor updates available
📖 DOCUMENTATION
✅ README: Current
✅ API docs: 95% coverage
⚠️ CLI reference: Needs update
✅ Changelog: Complete
📈 METRICS
├── Source code: 15,432 lines
├── Test code: 8,967 lines
├── Test ratio: 58% (excellent)
└── Complexity: Low (maintainable)
🎯 RECOMMENDATIONS:
1. Update CLI documentation
2. Refactor 3 complex functions
3. Update minor dependencies
4. Consider splitting large test files
🏆 PROJECT STATUS: Ready for v0.13.0 release!
```
## Health Scoring
### Excellent (90-100)
- All quality gates pass
- High test coverage (>95%)
- No security issues
- Documentation current
### Good (75-89)
- Minor issues present
- Good test coverage (>90%)
- No critical security issues
- Most documentation current
### Needs Attention (60-74)
- Several quality issues
- Adequate test coverage (>80%)
- Minor security concerns
- Documentation gaps
### Critical (<60)
- Major quality problems
- Low test coverage (<80%)
- Security vulnerabilities
- Significant documentation issues
## Context
- Provides comprehensive project overview
- Identifies potential issues before they become problems
- Tracks project health trends over time
- Helps prioritize maintenance tasks
- Supports release readiness decisions
+62
View File
@@ -0,0 +1,62 @@
# Basic Memory Custom Commands
This directory contains custom Claude Code slash commands for the Basic Memory project.
## Available Commands
### Release Management (`/project:release:*`)
- `/project:release:beta` - Create beta releases with automated quality checks
- `/project:release:release` - Create stable releases with comprehensive validation
- `/project:release:release-check` - Pre-flight validation without making changes
- `/project:release:changelog` - Generate changelog entries from commits
### Development (`/project:*`)
- `/project:test-coverage` - Run tests with detailed coverage analysis
- `/project:test-live` - Live testing suite using real Basic Memory installation
- `/project:lint-fix` - Run comprehensive linting with auto-fix
- `/project:check-health` - Comprehensive project health assessment
## Command Structure
Commands are organized by functionality:
```
.claude/commands/
├── release/ # Release management commands
│ ├── beta.md # /project:release:beta
│ ├── release.md # /project:release:release
│ ├── release-check.md # /project:release:release-check
│ └── changelog.md # /project:release:changelog
├── test-coverage.md # /project:test-coverage
├── test-live.md # /project:test-live
├── lint-fix.md # /project:lint-fix
├── check-health.md # /project:check-health
└── commands.md # This overview file
```
## Usage
Commands are invoked using the `/project:` prefix:
- `/project:release:beta v0.13.0b4`
- `/project:test-coverage mcp`
- `/project:test-live core`
- `/project:release:release-check`
- `/project:check-health`
## Implementation
Each command is implemented as a Markdown file containing structured prompts that:
1. Validate preconditions
2. Execute steps in the correct order
3. Handle errors gracefully
4. Provide clear status updates
5. Return actionable results
## Tooling Integration
Commands leverage existing project tooling:
- `make check` - Quality checks
- `make test` - Test suite
- `make update-deps` - Dependency updates
- `uv` - Package management
- `git` - Version control
- GitHub Actions - CI/CD pipeline
+145
View File
@@ -0,0 +1,145 @@
# /project:lint-fix - Comprehensive Code Quality Fix
Run comprehensive linting and auto-fix code quality issues across the codebase.
## Usage
```
/project:lint-fix
```
## Implementation
You are an expert code quality engineer for the Basic Memory project. When the user runs `/project:lint-fix`, execute the following steps:
### Step 1: Pre-flight Check
1. **Verify Clean Working Directory**
```bash
git status --porcelain
```
- Check for uncommitted changes
- Warn if working directory is not clean
- Suggest stashing changes if needed
### Step 2: Import Organization
1. **Fix Import Order and Cleanup**
```bash
uv run ruff check --select I --fix .
```
- Sort imports by category (standard, third-party, local)
- Remove unused imports
- Fix import spacing and organization
### Step 3: Code Formatting
1. **Apply Consistent Formatting**
```bash
uv run ruff format .
```
- Format code according to project style
- Fix line length issues (100 chars max)
- Standardize quotes and spacing
### Step 4: Linting with Auto-fix
1. **Fix Linting Issues**
```bash
uv run ruff check --fix .
```
- Auto-fix safe linting issues
- Report any remaining manual fixes needed
- Focus on code quality and best practices
### Step 5: Type Checking
1. **Validate Type Annotations**
```bash
uv run pyright
```
- Check for type errors
- Report any missing type annotations
- Validate type compatibility
### Step 6: Report Generation
Generate comprehensive quality report:
```
🔧 Code Quality Fix Report
✅ FIXES APPLIED:
├── Import organization: 12 files updated
├── Code formatting: 8 files reformatted
├── Auto-fixable lint issues: 23 issues resolved
└── Total files processed: 156
⚠️ MANUAL ATTENTION NEEDED:
├── Type annotations missing in entity_service.py:45
├── Complex function needs refactoring in sync_service.py:123
└── Unused variable in test_utils.py:67
🎯 QUALITY SCORE: 96.2% (excellent)
📁 Run `git diff` to review all changes
```
## Error Handling
### Common Issues
- **Merge Conflicts**: Provide resolution guidance
- **Syntax Errors**: Point to specific files and lines
- **Type Errors**: Suggest specific fixes
- **Import Errors**: Check for missing dependencies
### Recovery Steps
- If auto-fixes introduce issues, provide rollback instructions
- If type checking fails, suggest incremental fixes
- If tests break, provide debugging guidance
## Quality Gates
### Must Pass
- [ ] All auto-fixable lint issues resolved
- [ ] Code formatting consistent
- [ ] No syntax errors
- [ ] Import organization clean
### Should Pass (Warnings)
- [ ] No type checking errors
- [ ] No complex function warnings
- [ ] No unused variables/imports
- [ ] Consistent naming conventions
## Output Examples
### Successful Fix
```
🎉 CODE QUALITY IMPROVED!
✅ All auto-fixes applied successfully
📏 Code formatting: 100% compliant
🔍 Linting: No issues found
🏷️ Type checking: All passed
Ready for commit! Use:
git add -A && git commit -m "style: fix code quality issues"
```
### Issues Requiring Attention
```
⚠️ PARTIAL SUCCESS - MANUAL FIXES NEEDED
✅ Auto-fixes applied: 45 issues
❌ Manual fixes needed: 3 issues
Priority fixes:
1. Fix type annotation in services/entity_service.py:142
2. Simplify complex function in sync/sync_service.py:67
3. Remove unused import in tests/conftest.py:12
Run these commands:
# Fix specific file
uv run pyright src/basic_memory/services/entity_service.py
```
## Context
- Uses ruff for fast Python linting and formatting
- Uses pyright for type checking
- Follows project code style guidelines (100 char line length)
- Maintains backward compatibility
- Integrates with existing pre-commit hooks
+69
View File
@@ -0,0 +1,69 @@
# /beta - Create Beta Release
Create a new beta release for the current version with automated quality checks and tagging.
## Usage
```
/beta [version]
```
**Parameters:**
- `version` (optional): Beta version like `v0.13.0b4`. If not provided, auto-increments from latest beta tag.
## Implementation
You are an expert release manager for the Basic Memory project. When the user runs `/beta`, execute the following steps:
### Step 1: Pre-flight Checks
1. Check current git status for uncommitted changes
2. Verify we're on the `main` branch
3. Get the latest beta tag to determine next version if not provided
### Step 2: Quality Assurance
1. Run `make check` to ensure code quality
2. If any checks fail, report issues and stop
3. Run `make update-deps` to ensure latest dependencies
4. Commit any dependency updates with proper message
### Step 3: Version Determination
If version not provided:
1. Get latest git tags with `git tag -l "v*b*" --sort=-version:refname | head -1`
2. Auto-increment beta number (e.g., `v0.13.0b2``v0.13.0b3`)
3. Confirm version with user before proceeding
### Step 4: Release Creation
1. Commit any remaining changes
2. Push to main: `git push origin main`
3. Create tag: `git tag {version}`
4. Push tag: `git push origin {version}`
### Step 5: Monitor Release
1. Check GitHub Actions workflow starts successfully
2. Provide installation instructions for beta
3. Report status and next steps
## Error Handling
- If quality checks fail, provide specific fix instructions
- If git operations fail, provide manual recovery steps
- If GitHub Actions fail, provide debugging guidance
## Success Output
```
✅ Beta Release v0.13.0b4 Created Successfully!
🏷️ Tag: v0.13.0b4
🚀 GitHub Actions: Running
📦 PyPI: Will be available in ~5 minutes
Install with:
uv tool upgrade basic-memory --prerelease=allow
Monitor release: https://github.com/basicmachines-co/basic-memory/actions
```
## Context
- Use the existing Makefile targets (`make check`, `make update-deps`)
- Follow semantic versioning for beta releases
- Maintain release notes in CHANGELOG.md
- Use conventional commit messages
- Leverage uv-dynamic-versioning for version management
+157
View File
@@ -0,0 +1,157 @@
# /changelog - Generate or Update Changelog Entry
Analyze commits and generate formatted changelog entry for a version.
## Usage
```
/changelog <version> [type]
```
**Parameters:**
- `version` (required): Version like `v0.13.0` or `v0.13.0b4`
- `type` (optional): `beta`, `rc`, or `stable` (default: `stable`)
## Implementation
You are an expert technical writer for the Basic Memory project. When the user runs `/changelog`, execute the following steps:
### Step 1: Version Analysis
1. **Determine Commit Range**
```bash
# Find last release tag
git tag -l "v*" --sort=-version:refname | grep -v "b\|rc" | head -1
# Get commits since last release
git log --oneline ${last_tag}..HEAD
```
2. **Parse Conventional Commits**
- Extract feat: (features)
- Extract fix: (bug fixes)
- Extract BREAKING CHANGE: (breaking changes)
- Extract chore:, docs:, test: (other improvements)
### Step 2: Categorize Changes
1. **Features (feat:)**
- New MCP tools
- New CLI commands
- New API endpoints
- Major functionality additions
2. **Bug Fixes (fix:)**
- User-facing bug fixes
- Critical issues resolved
- Performance improvements
- Security fixes
3. **Technical Improvements**
- Test coverage improvements
- Code quality enhancements
- Dependency updates
- Documentation updates
4. **Breaking Changes**
- API changes
- Configuration changes
- Behavior changes
- Migration requirements
### Step 3: Generate Changelog Entry
Create formatted entry following existing CHANGELOG.md style:
```markdown
## v0.13.0 (2025-06-03)
### Features
- **Multi-Project Management System** - Switch between projects instantly during conversations
([`993e88a`](https://github.com/basicmachines-co/basic-memory/commit/993e88a))
- Instant project switching with session context
- Project-specific operations and isolation
- Project discovery and management tools
- **Advanced Note Editing** - Incremental editing with append, prepend, find/replace, and section operations
([`6fc3904`](https://github.com/basicmachines-co/basic-memory/commit/6fc3904))
- `edit_note` tool with multiple operation types
- Smart frontmatter-aware editing
- Validation and error handling
### Bug Fixes
- **#118**: Fix YAML tag formatting to follow standard specification
([`2dc7e27`](https://github.com/basicmachines-co/basic-memory/commit/2dc7e27))
- **#110**: Make --project flag work consistently across CLI commands
([`02dd91a`](https://github.com/basicmachines-co/basic-memory/commit/02dd91a))
### Technical Improvements
- **Comprehensive Testing** - 100% test coverage with integration testing
([`468a22f`](https://github.com/basicmachines-co/basic-memory/commit/468a22f))
- MCP integration test suite
- End-to-end testing framework
- Performance and edge case validation
### Breaking Changes
- **Database Migration**: Automatic migration from per-project to unified database.
Data will be re-index from the filesystem, resulting in no data loss.
- **Configuration Changes**: Projects now synced between config.json and database
- **Full Backward Compatibility**: All existing setups continue to work seamlessly
```
### Step 4: Integration
1. **Update CHANGELOG.md**
- Insert new entry at top
- Maintain consistent formatting
- Include commit links and issue references
2. **Validation**
- Check all major changes are captured
- Verify commit links work
- Ensure issue numbers are correct
## Smart Analysis Features
### Automatic Classification
- Detect feature additions from file changes
- Identify bug fixes from commit messages
- Find breaking changes from code analysis
- Extract issue numbers from commit messages
### Content Enhancement
- Add context for technical changes
- Include migration guidance for breaking changes
- Suggest installation/upgrade instructions
- Link to relevant documentation
## Output Format
### For Beta Releases
```markdown
## v0.13.0b4 (2025-06-03)
### Beta Changes Since v0.13.0b3
- Fix FastMCP API compatibility issues
- Update dependencies to latest versions
- Resolve setuptools import error
### Installation
```bash
uv tool install basic-memory --prerelease=allow
```
### Known Issues
- [List any known issues for beta testing]
```
### For Stable Releases
Full changelog with complete feature list, organized by impact and category.
## Context
- Follows existing CHANGELOG.md format and style
- Uses conventional commit standards
- Includes GitHub commit links for traceability
- Focuses on user-facing changes and value
- Maintains consistency with previous entries
+131
View File
@@ -0,0 +1,131 @@
# /release-check - Pre-flight Release Validation
Comprehensive pre-flight check for release readiness without making any changes.
## Usage
```
/release-check [version]
```
**Parameters:**
- `version` (optional): Version to validate like `v0.13.0`. If not provided, determines from context.
## Implementation
You are an expert QA engineer for the Basic Memory project. When the user runs `/release-check`, execute the following validation steps:
### Step 1: Environment Validation
1. **Git Status Check**
- Verify working directory is clean
- Confirm on `main` branch
- Check if ahead/behind origin
2. **Version Validation**
- Validate version format if provided
- Check for existing tags with same version
- Verify version increments properly from last release
### Step 2: Code Quality Gates
1. **Test Suite Validation**
```bash
make test
```
- All tests must pass
- Check test coverage (target: 95%+)
- Validate no skipped critical tests
2. **Code Quality Checks**
```bash
make lint
make type-check
```
- No linting errors
- No type checking errors
- Code formatting is consistent
### Step 3: Documentation Validation
1. **Changelog Check**
- CHANGELOG.md contains entry for target version
- Entry includes all major features and fixes
- Breaking changes are documented
2. **Documentation Currency**
- README.md reflects current functionality
- CLI reference is up to date
- MCP tools are documented
### Step 4: Dependency Validation
1. **Security Scan**
- No known vulnerabilities in dependencies
- All dependencies are at appropriate versions
- No conflicting dependency versions
2. **Build Validation**
- Package builds successfully
- All required files are included
- No missing dependencies
### Step 5: Issue Tracking Validation
1. **GitHub Issues Check**
- No critical open issues blocking release
- All milestone issues are resolved
- High-priority bugs are fixed
2. **Testing Coverage**
- Integration tests pass
- MCP tool tests pass
- Cross-platform compatibility verified
## Report Format
Generate a comprehensive report:
```
🔍 Release Readiness Check for v0.13.0
✅ PASSED CHECKS:
├── Git status clean
├── On main branch
├── All tests passing (744/744)
├── Test coverage: 98.2%
├── Type checking passed
├── Linting passed
├── CHANGELOG.md updated
└── No critical issues open
⚠️ WARNINGS:
├── 2 medium-priority issues still open
└── Documentation could be updated
❌ BLOCKING ISSUES:
└── None found
🎯 RELEASE READINESS: ✅ READY
Recommended next steps:
1. Address warnings if desired
2. Run `/release v0.13.0` when ready
```
## Validation Criteria
### Must Pass (Blocking)
- [ ] All tests pass
- [ ] No type errors
- [ ] No linting errors
- [ ] Working directory clean
- [ ] On main branch
- [ ] CHANGELOG.md has version entry
- [ ] No critical open issues
### Should Pass (Warnings)
- [ ] Test coverage >95%
- [ ] No medium-priority open issues
- [ ] Documentation up to date
- [ ] No dependency vulnerabilities
## Context
- This is a read-only validation - makes no changes
- Provides confidence before running actual release
- Helps identify issues early in release process
- Can be run multiple times safely
+84
View File
@@ -0,0 +1,84 @@
# /release - Create Stable Release
Create a stable release from the current main branch with comprehensive validation.
## Usage
```
/release <version>
```
**Parameters:**
- `version` (required): Release version like `v0.13.0`
## Implementation
You are an expert release manager for the Basic Memory project. When the user runs `/release`, execute the following steps:
### Step 1: Pre-flight Validation
1. Verify version format matches `v\d+\.\d+\.\d+` pattern
2. Check current git status for uncommitted changes
3. Verify we're on the `main` branch
4. Confirm no existing tag with this version
### Step 2: Comprehensive Quality Checks
1. Run `make check` (lint, format, type-check, full test suite)
2. Verify test coverage meets minimum requirements (95%+)
3. Check that CHANGELOG.md contains entry for this version
4. Validate all high-priority issues are closed
### Step 3: Release Preparation
1. Update any version references if needed
2. Commit any final changes with message: `chore: prepare for ${version} release`
3. Push to main: `git push origin main`
### Step 4: Release Creation
1. Create annotated tag: `git tag -a ${version} -m "Release ${version}"`
2. Push tag: `git push origin ${version}`
3. Monitor GitHub Actions for release automation
### Step 5: Post-Release Validation
1. Verify GitHub release is created automatically
2. Check PyPI publication
3. Validate release assets
4. Test installation: `uv tool install basic-memory`
### Step 6: Documentation Update
1. Update any post-release documentation
2. Create follow-up tasks if needed
## Pre-conditions Check
Before starting, verify:
- [ ] All beta testing is complete
- [ ] Critical bugs are fixed
- [ ] Breaking changes are documented
- [ ] CHANGELOG.md is updated
- [ ] Version number follows semantic versioning
## Error Handling
- If any quality check fails, stop and provide fix instructions
- If changelog entry missing, prompt to create one
- If tests fail, provide debugging guidance
- If GitHub Actions fail, provide manual release steps
## Success Output
```
🎉 Stable Release v0.13.0 Created Successfully!
🏷️ Tag: v0.13.0
📋 GitHub Release: https://github.com/basicmachines-co/basic-memory/releases/tag/v0.13.0
📦 PyPI: https://pypi.org/project/basic-memory/0.13.0/
🚀 GitHub Actions: Completed
Install with:
uv tool install basic-memory
Users can now upgrade:
uv tool upgrade basic-memory
```
## Context
- This creates production releases used by end users
- Must pass all quality gates before proceeding
- Follows the release workflow documented in CLAUDE.md
- Uses uv-dynamic-versioning for automatic version management
- Triggers automated GitHub release with changelog
+131
View File
@@ -0,0 +1,131 @@
# /test-coverage - Run Tests with Coverage Analysis
Execute test suite with comprehensive coverage reporting and analysis.
## Usage
```
/test-coverage [pattern]
```
**Parameters:**
- `pattern` (optional): Test pattern to run specific tests (e.g., `test_mcp`, `*integration*`)
## Implementation
You are an expert QA engineer for the Basic Memory project. When the user runs `/test-coverage`, execute the following steps:
### Step 1: Test Execution
1. **Run Tests with Coverage**
```bash
# Full test suite
uv run pytest --cov=basic_memory --cov-report=html --cov-report=term -v
# Or with pattern if provided
uv run pytest tests/*{pattern}* --cov=basic_memory --cov-report=html --cov-report=term -v
```
2. **Generate Coverage Reports**
- Terminal summary with percentages
- HTML report for detailed analysis
- Identify files below coverage threshold
### Step 2: Coverage Analysis
1. **Summary Statistics**
- Overall coverage percentage
- Number of files with 100% coverage
- Files below 95% threshold
- Total lines covered/missed
2. **Detailed Breakdown**
- Coverage by module/package
- Identify untested code paths
- Find missing edge case tests
### Step 3: Report Generation
Generate comprehensive coverage report:
```
🧪 Test Coverage Report
📊 OVERALL COVERAGE: 98.2% (target: 95%+)
✅ EXCELLENT COVERAGE (>95%):
├── basic_memory/mcp/: 99.1%
├── basic_memory/services/: 98.8%
├── basic_memory/repository/: 97.9%
└── basic_memory/api/: 96.2%
⚠️ NEEDS ATTENTION (<95%):
├── basic_memory/sync/: 94.1% (missing 12 lines)
└── basic_memory/importers/: 91.8% (missing 23 lines)
🎯 SPECIFIC GAPS:
├── sync_service.py:142-145 (error handling)
├── importer_base.py:67-70 (edge case)
└── file_utils.py:89 (exception path)
📁 HTML Report: htmlcov/index.html
🚀 Run `open htmlcov/index.html` to view detailed report
```
### Step 4: Actionable Recommendations
1. **Coverage Improvements**
- Suggest specific tests to add
- Identify edge cases to cover
- Recommend integration tests
2. **Quality Insights**
- Highlight well-tested modules
- Point out testing patterns to follow
- Suggest refactoring for testability
## Advanced Analysis
### Performance Metrics
- Test execution time by module
- Slowest tests identification
- Coverage collection overhead
### Integration Coverage
- MCP tool integration tests
- API endpoint coverage
- Database operation coverage
- File system operation coverage
## Output Examples
### Full Coverage Success
```
🎉 EXCELLENT COVERAGE!
📊 Coverage: 98.7% (744 tests passed)
✅ All modules above 95% threshold
🏆 23 files with 100% coverage
⚡ Tests completed in 45.2s
Ready for release! 🚀
```
### Coverage Issues Found
```
⚠️ COVERAGE GAPS DETECTED
📊 Coverage: 92.1% (below 95% target)
❌ 3 modules need attention
🔍 43 uncovered lines found
Priority fixes:
1. Add tests for error handling in sync_service.py
2. Cover edge cases in importer_base.py
3. Test exception paths in file_utils.py
Run specific tests:
uv run pytest tests/sync/ -v
```
## Context
- Uses pytest with coverage plugin
- Generates both terminal and HTML reports
- Focuses on actionable improvement suggestions
- Integrates with existing test infrastructure
- Helps maintain high code quality standards
+397
View File
@@ -0,0 +1,397 @@
# /project:test-live - Live Basic Memory Testing Suite
Execute comprehensive real-world testing of Basic Memory using the installed version, following the methodology in TESTING.md. All test results are recorded as notes in a dedicated test project.
## Usage
```
/project:test-live [phase]
```
**Parameters:**
- `phase` (optional): Specific test phase to run (`core`, `features`, `edge`, `workflows`, `stress`, or `all`)
## Implementation
You are an expert QA engineer conducting live testing of Basic Memory. When the user runs `/project:test-live`, execute comprehensive testing following the TESTING.md methodology:
### Pre-Test Setup
1. **Environment Verification**
- Verify basic-memory is installed and accessible via MCP
- Check version and confirm it's the expected release
- Test MCP connection and tool availability
2. **Test Project Creation**
```
Create project: "basic-memory-testing-[timestamp]"
Location: ~/basic-memory-testing-[timestamp]
Purpose: Record all test observations and results
```
3. **Baseline Documentation**
Create initial test session note with:
- Test environment details
- Version being tested
- Test objectives and scope
- Start timestamp
### Phase 1: Core Functionality Validation
Test all fundamental MCP tools systematically:
**write_note Tests:**
- Basic note creation with various content types
- Frontmatter handling (tags, custom fields)
- Special characters in titles and content
- Unicode and emoji support
- Empty notes and minimal content
**read_note Tests:**
- Read by title, permalink, memory:// URLs
- Non-existent notes (error handling)
- Notes with complex formatting
- Performance with large notes
**search_notes Tests:**
- Simple text queries
- Tag-based searches
- Boolean operators and complex queries
- Empty/no results scenarios
- Performance with growing knowledge base
**Recent Activity Tests:**
- Various timeframes ("today", "1 week", "1d")
- Type filtering (if available)
- Empty project scenarios
- Performance with many recent changes
**Context Building Tests:**
- Different depth levels (1, 2, 3+)
- Various timeframes
- Relation traversal accuracy
- Performance with complex graphs
### Phase 2: v0.13.0 Feature Deep Dive
**Project Management:**
- Create multiple projects dynamically
- Switch between projects mid-conversation
- Cross-project operations
- Project discovery and status
- Default project behavior
- Invalid project handling
**Advanced Note Editing:**
- `edit_note` with append operations
- Prepend operations
- Find/replace with validation
- Section replacement under headers
- Error scenarios (invalid operations)
- Frontmatter preservation
**File Management:**
- `move_note` within same project
- Move between projects
- Automatic folder creation
- Special characters in paths
- Database consistency validation
- Search index updates after moves
### Phase 3: Edge Case Exploration
**Boundary Testing:**
- Very long titles and content (stress limits)
- Empty projects and notes
- Unicode, emojis, special symbols
- Deeply nested folder structures
- Circular relations and self-references
- Maximum relation depths
**Error Scenarios:**
- Invalid memory:// URLs
- Missing files referenced in database
- Invalid project names and paths
- Malformed note structures
- Concurrent operation conflicts
**Performance Testing:**
- Create 100+ notes rapidly
- Complex search queries
- Deep relation chains (5+ levels)
- Rapid successive operations
- Memory usage monitoring
### Phase 4: Real-World Workflow Scenarios
**Meeting Notes Pipeline:**
1. Create meeting notes with action items
2. Extract action items using edit_note
3. Build relations to project documents
4. Update progress incrementally
5. Search and track completion
**Research Knowledge Building:**
1. Create research topic hierarchy
2. Build complex relation networks
3. Add incremental findings over time
4. Search for connections and patterns
5. Reorganize as knowledge evolves
**Multi-Project Workflow:**
1. Technical documentation project
2. Personal recipe collection project
3. Learning/course notes project
4. Switch contexts during conversation
5. Cross-reference related concepts
**Content Evolution:**
1. Start with basic notes
2. Enhance with relations and observations
3. Reorganize file structure using moves
4. Update content with edit operations
5. Validate knowledge graph integrity
### Phase 5: Creative Stress Testing
**Creative Exploration:**
- Rapid project creation/switching patterns
- Unusual but valid markdown structures
- Creative observation categories
- Novel relation types and patterns
- Unexpected tool combinations
**Stress Scenarios:**
- Bulk operations (many notes quickly)
- Complex nested moves and edits
- Deep context building
- Complex boolean search expressions
- Resource constraint testing
## Test Observation Format
Record ALL observations immediately as Basic Memory notes:
```markdown
---
title: Test Session [Phase] YYYY-MM-DD HH:MM
tags: [testing, v0.13.0, live-testing, [phase]]
permalink: test-session-[phase]-[timestamp]
---
# Test Session [Phase] - [Date/Time]
## Environment
- Basic Memory version: [version]
- MCP connection: [status]
- Test project: [name]
- Phase focus: [description]
## Test Results
### ✅ Successful Operations
- [timestamp] write_note: Created note with emoji title 📝 #functionality
- [timestamp] search_notes: Boolean query returned 23 results in 0.4s #performance
- [timestamp] edit_note: Append operation preserved frontmatter #reliability
### ⚠️ Issues Discovered
- [timestamp] move_note: Slow with deep folder paths (2.1s) #performance
- [timestamp] search_notes: Unicode query returned unexpected results #bug
- [timestamp] project switch: Context lost for memory:// URLs #issue
### 🚀 Enhancements Identified
- edit_note could benefit from preview mode #ux-improvement
- search_notes needs fuzzy matching for typos #feature-idea
- move_note could auto-suggest folder creation #usability
### 📊 Performance Metrics
- Average write_note time: 0.3s
- Search with 100+ notes: 0.6s
- Project switch overhead: 0.1s
- Memory usage: [observed levels]
## Relations
- tests [[Basic Memory v0.13.0]]
- part_of [[Live Testing Suite]]
- found_issues [[Bug Report: Unicode Search]]
- discovered [[Performance Optimization Opportunities]]
```
## Quality Assessment Areas
**User Experience & Usability:**
- Tool instruction clarity and examples
- Error message actionability
- Response time acceptability
- Tool consistency and discoverability
- Learning curve and intuitiveness
**System Behavior:**
- Context preservation across operations
- memory:// URL navigation reliability
- Multi-step workflow cohesion
- Edge case graceful handling
- Recovery from user errors
**Documentation Alignment:**
- Tool output clarity and helpfulness
- Behavior vs. documentation accuracy
- Example validity and usefulness
- Real-world vs. documented workflows
**Mental Model Validation:**
- Natural user expectation alignment
- Surprising behavior identification
- Mistake recovery ease
- Knowledge graph concept naturalness
**Performance & Reliability:**
- Operation completion times
- Consistency across sessions
- Scaling behavior with growth
- Unexpected slowness identification
## Error Documentation Protocol
For each error discovered:
1. **Immediate Recording**
- Create dedicated error note
- Include exact reproduction steps
- Capture error messages verbatim
- Note system state when error occurred
2. **Error Note Format**
```markdown
---
title: Bug Report - [Short Description]
tags: [bug, testing, v0.13.0, [severity]]
---
# Bug Report: [Description]
## Reproduction Steps
1. [Exact steps to reproduce]
2. [Include all parameters used]
3. [Note any special conditions]
## Expected Behavior
[What should have happened]
## Actual Behavior
[What actually happened]
## Error Messages
```
[Exact error text]
```
## Environment
- Version: [version]
- Project: [name]
- Timestamp: [when]
## Severity
- [ ] Critical (blocks major functionality)
- [ ] High (impacts user experience)
- [ ] Medium (workaround available)
- [ ] Low (minor inconvenience)
## Relations
- discovered_during [[Test Session [Phase]]]
- affects [[Feature Name]]
```
## Success Metrics Tracking
**Quantitative Measures:**
- Test scenario completion rate
- Bug discovery count with severity
- Performance benchmark establishment
- Tool coverage completeness
**Qualitative Measures:**
- Conversation flow naturalness
- Knowledge graph quality
- User experience insights
- System reliability assessment
## Test Execution Flow
1. **Setup Phase** (5 minutes)
- Verify environment and create test project
- Record baseline system state
- Establish performance benchmarks
2. **Core Testing** (15-20 minutes per phase)
- Execute test scenarios systematically
- Record observations immediately
- Note timestamps for performance tracking
- Explore variations when interesting behaviors occur
3. **Documentation** (5 minutes per phase)
- Create phase summary note
- Link related test observations
- Update running issues list
- Record enhancement ideas
4. **Analysis Phase** (10 minutes)
- Review all observations across phases
- Identify patterns and trends
- Create comprehensive summary report
- Generate development recommendations
## Expected Outcomes
**System Validation:**
- v0.13.0 feature verification in real usage
- Edge case discovery beyond unit tests
- Performance baseline establishment
- Bug identification with reproduction cases
**Knowledge Base Creation:**
- Comprehensive testing documentation
- Real usage examples for user guides
- Edge case scenarios for future testing
- Performance insights for optimization
**Development Insights:**
- Prioritized bug fix list
- Enhancement ideas from real usage
- Architecture validation results
- User experience improvement areas
## Post-Test Deliverables
1. **Test Summary Note**
- Overall results and findings
- Critical issues requiring immediate attention
- Enhancement opportunities discovered
- System readiness assessment
2. **Bug Report Collection**
- All discovered issues with reproduction steps
- Severity and impact assessments
- Suggested fixes where applicable
3. **Performance Baseline**
- Timing data for all operations
- Scaling behavior observations
- Resource usage patterns
4. **UX Improvement Recommendations**
- Usability enhancement suggestions
- Documentation improvement areas
- Tool design optimization ideas
5. **Updated TESTING.md**
- Incorporate new test scenarios discovered
- Update based on real execution experience
- Add performance benchmarks and targets
## Context
- Uses installed basic-memory version (not development)
- Tests complete MCP→API→DB→File stack
- Creates living documentation in Basic Memory itself
- Follows integration over isolation philosophy
- Focuses on real usage patterns over checklist validation
- Generates actionable insights for development team
@@ -0,0 +1,104 @@
"""project constraint fix
Revision ID: 647e7a75e2cd
Revises: 5fe1ab1ccebe
Create Date: 2025-06-03 12:48:30.162566
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = "647e7a75e2cd"
down_revision: Union[str, None] = "5fe1ab1ccebe"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Remove the problematic UNIQUE constraint on is_default column.
The UNIQUE constraint prevents multiple projects from having is_default=FALSE,
which breaks project creation when the service sets is_default=False.
Since SQLite doesn't support dropping specific constraints easily, we'll
recreate the table without the problematic constraint.
"""
# For SQLite, we need to recreate the table without the UNIQUE constraint
# Create a new table without the UNIQUE constraint on is_default
op.create_table(
"project_new",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("name", sa.String(), nullable=False),
sa.Column("description", sa.Text(), nullable=True),
sa.Column("permalink", sa.String(), nullable=False),
sa.Column("path", sa.String(), nullable=False),
sa.Column("is_active", sa.Boolean(), nullable=False),
sa.Column("is_default", sa.Boolean(), nullable=True), # No UNIQUE constraint!
sa.Column("created_at", sa.DateTime(), nullable=False),
sa.Column("updated_at", sa.DateTime(), nullable=False),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint("name"),
sa.UniqueConstraint("permalink"),
)
# Copy data from old table to new table
op.execute("INSERT INTO project_new SELECT * FROM project")
# Drop the old table
op.drop_table("project")
# Rename the new table
op.rename_table("project_new", "project")
# Recreate the indexes
with op.batch_alter_table("project", schema=None) as batch_op:
batch_op.create_index("ix_project_created_at", ["created_at"], unique=False)
batch_op.create_index("ix_project_name", ["name"], unique=True)
batch_op.create_index("ix_project_path", ["path"], unique=False)
batch_op.create_index("ix_project_permalink", ["permalink"], unique=True)
batch_op.create_index("ix_project_updated_at", ["updated_at"], unique=False)
def downgrade() -> None:
"""Add back the UNIQUE constraint on is_default column.
WARNING: This will break project creation again if multiple projects
have is_default=FALSE.
"""
# Recreate the table with the UNIQUE constraint
op.create_table(
"project_old",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("name", sa.String(), nullable=False),
sa.Column("description", sa.Text(), nullable=True),
sa.Column("permalink", sa.String(), nullable=False),
sa.Column("path", sa.String(), nullable=False),
sa.Column("is_active", sa.Boolean(), nullable=False),
sa.Column("is_default", sa.Boolean(), nullable=True),
sa.Column("created_at", sa.DateTime(), nullable=False),
sa.Column("updated_at", sa.DateTime(), nullable=False),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint("is_default"), # Add back the problematic constraint
sa.UniqueConstraint("name"),
sa.UniqueConstraint("permalink"),
)
# Copy data (this may fail if multiple FALSE values exist)
op.execute("INSERT INTO project_old SELECT * FROM project")
# Drop the current table and rename
op.drop_table("project")
op.rename_table("project_old", "project")
# Recreate indexes
with op.batch_alter_table("project", schema=None) as batch_op:
batch_op.create_index("ix_project_created_at", ["created_at"], unique=False)
batch_op.create_index("ix_project_name", ["name"], unique=True)
batch_op.create_index("ix_project_path", ["path"], unique=False)
batch_op.create_index("ix_project_permalink", ["permalink"], unique=True)
batch_op.create_index("ix_project_updated_at", ["updated_at"], unique=False)
@@ -111,10 +111,9 @@ async def add_project(
Response confirming the project was added
"""
try: # pragma: no cover
await project_service.add_project(project_data.name, project_data.path)
if project_data.set_default: # pragma: no cover
await project_service.set_default_project(project_data.name)
await project_service.add_project(
project_data.name, project_data.path, set_default=project_data.set_default
)
return ProjectStatusResponse( # pyright: ignore [reportCallIssue]
message=f"Project '{project_data.name}' added successfully",
+1 -3
View File
@@ -49,9 +49,7 @@ class Project(Base):
# Status flags
is_active: Mapped[bool] = mapped_column(Boolean, default=True)
is_default: Mapped[Optional[bool]] = mapped_column(
Boolean, default=None, unique=True, nullable=True
)
is_default: Mapped[Optional[bool]] = mapped_column(Boolean, default=None, nullable=True)
# Timestamps
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
+63 -12
View File
@@ -67,12 +67,13 @@ class ProjectService:
"""Get the file path for a project by name."""
return await self.repository.get_by_name(name)
async def add_project(self, name: str, path: str) -> None:
async def add_project(self, name: str, path: str, set_default: bool = False) -> None:
"""Add a new project to the configuration and database.
Args:
name: The name of the project
path: The file path to the project directory
set_default: Whether to set this project as the default
Raises:
ValueError: If the project already exists
@@ -92,9 +93,16 @@ class ProjectService:
"path": resolved_path,
"permalink": generate_permalink(project_config.name),
"is_active": True,
"is_default": False,
# Don't set is_default=False to avoid UNIQUE constraint issues
# Let it default to NULL, only set to True when explicitly making default
}
await self.repository.create(project_data)
created_project = await self.repository.create(project_data)
# If this should be the default project, ensure only one default exists
if set_default:
await self.repository.set_as_default(created_project.id)
config_manager.set_default_project(name)
logger.info(f"Project '{name}' set as default")
logger.info(f"Project '{name}' added at {resolved_path}")
@@ -144,6 +152,45 @@ class ProjectService:
logger.info(f"Project '{name}' set as default in configuration and database")
async def _ensure_single_default_project(self) -> None:
"""Ensure only one project has is_default=True.
This method validates the database state and fixes any issues where
multiple projects might have is_default=True or no project is marked as default.
"""
if not self.repository:
raise ValueError("Repository is required for _ensure_single_default_project") # pragma: no cover
# Get all projects with is_default=True
db_projects = await self.repository.find_all()
default_projects = [p for p in db_projects if p.is_default is True]
if len(default_projects) > 1: # pragma: no cover
# Multiple defaults found - fix by keeping the first one and clearing others
# This is defensive code that should rarely execute due to business logic enforcement
logger.warning( # pragma: no cover
f"Found {len(default_projects)} projects with is_default=True, fixing..."
)
keep_default = default_projects[0] # pragma: no cover
# Clear all defaults first, then set only the first one as default
await self.repository.set_as_default(keep_default.id) # pragma: no cover
logger.info(
f"Fixed default project conflicts, kept '{keep_default.name}' as default"
) # pragma: no cover
elif len(default_projects) == 0: # pragma: no cover
# No default project - set the config default as default
# This is defensive code for edge cases where no default exists
config_default = config_manager.default_project # pragma: no cover
config_project = await self.repository.get_by_name(config_default) # pragma: no cover
if config_project: # pragma: no cover
await self.repository.set_as_default(config_project.id) # pragma: no cover
logger.info(
f"Set '{config_default}' as default project (was missing)"
) # pragma: no cover
async def synchronize_projects(self) -> None: # pragma: no cover
"""Synchronize projects between database and configuration.
@@ -172,7 +219,7 @@ class ProjectService:
"path": path,
"permalink": name.lower().replace(" ", "-"),
"is_active": True,
"is_default": (name == config_manager.default_project),
# Don't set is_default here - let the enforcement logic handle it
}
await self.repository.create(project_data)
@@ -182,19 +229,23 @@ class ProjectService:
logger.info(f"Adding project '{name}' to configuration")
config_manager.add_project(name, project.path)
# Make sure default project is synchronized
db_default = next((p for p in db_projects if p.is_default), None)
# Ensure database default project state is consistent
await self._ensure_single_default_project()
# Make sure default project is synchronized between config and database
db_default = await self.repository.get_default_project()
config_default = config_manager.default_project
if db_default and db_default.name != config_default:
# Update config to match DB default
logger.info(f"Updating default project in config to '{db_default.name}'")
config_manager.set_default_project(db_default.name)
elif not db_default and config_default in db_projects_by_name:
# Update DB to match config default
logger.info(f"Updating default project in database to '{config_default}'")
project = db_projects_by_name[config_default]
await self.repository.set_as_default(project.id)
elif not db_default and config_default:
# Update DB to match config default (if the project exists)
project = await self.repository.get_by_name(config_default)
if project:
logger.info(f"Updating default project in database to '{config_default}'")
await self.repository.set_as_default(project.id)
logger.info("Project synchronization complete")
@@ -546,4 +597,4 @@ class ProjectService:
database_size=db_size_readable,
watch_status=watch_status,
timestamp=datetime.now(),
)
)
+165
View File
@@ -304,3 +304,168 @@ async def test_set_default_project_config_db_mismatch(
# Clean up
if test_project_name in project_service.projects:
config_manager.remove_project(test_project_name)
@pytest.mark.asyncio
async def test_add_project_with_set_default_true(project_service: ProjectService, tmp_path):
"""Test adding a project with set_default=True enforces single default."""
test_project_name = f"test-default-true-{os.urandom(4).hex()}"
test_project_path = str(tmp_path / "test-default-true")
# Make sure the test directory exists
os.makedirs(test_project_path, exist_ok=True)
original_default = project_service.default_project
try:
# Get original default project from database
original_default_project = await project_service.repository.get_by_name(original_default)
# Add project with set_default=True
await project_service.add_project(test_project_name, test_project_path, set_default=True)
# Verify new project is set as default in both config and database
assert project_service.default_project == test_project_name
new_project = await project_service.repository.get_by_name(test_project_name)
assert new_project is not None
assert new_project.is_default is True
# Verify original default is no longer default in database
if original_default_project:
refreshed_original = await project_service.repository.get_by_name(original_default)
assert refreshed_original.is_default is not True
# Verify only one project has is_default=True
all_projects = await project_service.repository.find_all()
default_projects = [p for p in all_projects if p.is_default is True]
assert len(default_projects) == 1
assert default_projects[0].name == test_project_name
finally:
# Restore original default
if original_default:
await project_service.set_default_project(original_default)
# Clean up test project
if test_project_name in project_service.projects:
await project_service.remove_project(test_project_name)
@pytest.mark.asyncio
async def test_add_project_with_set_default_false(project_service: ProjectService, tmp_path):
"""Test adding a project with set_default=False doesn't change defaults."""
test_project_name = f"test-default-false-{os.urandom(4).hex()}"
test_project_path = str(tmp_path / "test-default-false")
# Make sure the test directory exists
os.makedirs(test_project_path, exist_ok=True)
original_default = project_service.default_project
try:
# Add project with set_default=False (explicit)
await project_service.add_project(test_project_name, test_project_path, set_default=False)
# Verify default project hasn't changed
assert project_service.default_project == original_default
# Verify new project is NOT set as default
new_project = await project_service.repository.get_by_name(test_project_name)
assert new_project is not None
assert new_project.is_default is not True
# Verify original default is still default
original_default_project = await project_service.repository.get_by_name(original_default)
if original_default_project:
assert original_default_project.is_default is True
finally:
# Clean up test project
if test_project_name in project_service.projects:
await project_service.remove_project(test_project_name)
@pytest.mark.asyncio
async def test_add_project_default_parameter_omitted(project_service: ProjectService, tmp_path):
"""Test adding a project without set_default parameter defaults to False behavior."""
test_project_name = f"test-default-omitted-{os.urandom(4).hex()}"
test_project_path = str(tmp_path / "test-default-omitted")
# Make sure the test directory exists
os.makedirs(test_project_path, exist_ok=True)
original_default = project_service.default_project
try:
# Add project without set_default parameter (should default to False)
await project_service.add_project(test_project_name, test_project_path)
# Verify default project hasn't changed
assert project_service.default_project == original_default
# Verify new project is NOT set as default
new_project = await project_service.repository.get_by_name(test_project_name)
assert new_project is not None
assert new_project.is_default is not True
finally:
# Clean up test project
if test_project_name in project_service.projects:
await project_service.remove_project(test_project_name)
@pytest.mark.asyncio
async def test_ensure_single_default_project_enforcement_logic(project_service: ProjectService):
"""Test that _ensure_single_default_project logic works correctly."""
# Test that the method exists and is callable
assert hasattr(project_service, "_ensure_single_default_project")
assert callable(getattr(project_service, "_ensure_single_default_project"))
# Call the enforcement method - should work without error
await project_service._ensure_single_default_project()
# Verify there is exactly one default project after enforcement
all_projects = await project_service.repository.find_all()
default_projects = [p for p in all_projects if p.is_default is True]
assert len(default_projects) == 1 # Should have exactly one default
@pytest.mark.asyncio
async def test_synchronize_projects_calls_ensure_single_default(
project_service: ProjectService, tmp_path
):
"""Test that synchronize_projects calls _ensure_single_default_project."""
test_project_name = f"test-sync-default-{os.urandom(4).hex()}"
test_project_path = str(tmp_path / "test-sync-default")
# Make sure the test directory exists
os.makedirs(test_project_path, exist_ok=True)
try:
# Add project to config only (simulating unsynchronized state)
from basic_memory.config import config_manager
config_manager.add_project(test_project_name, test_project_path)
# Verify it's in config but not in database
assert test_project_name in project_service.projects
db_project = await project_service.repository.get_by_name(test_project_name)
assert db_project is None
# Call synchronize_projects (this should call _ensure_single_default_project)
await project_service.synchronize_projects()
# Verify project is now in database
db_project = await project_service.repository.get_by_name(test_project_name)
assert db_project is not None
# Verify default project enforcement was applied
all_projects = await project_service.repository.find_all()
default_projects = [p for p in all_projects if p.is_default is True]
assert len(default_projects) <= 1 # Should be exactly 1 or 0
finally:
# Clean up test project
if test_project_name in project_service.projects:
await project_service.remove_project(test_project_name)