Files
markitect-main/tests/test_issue_51_outline_mode.py
tegwick 60f33443ae
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
feat(schema): add semantic schema generation as default mode
schema-generate now builds content-aware schemas from the document's
section hierarchy instead of counting markdown syntax elements. Detects
key-value tables, data tables, link lists, and mixed content patterns
to produce schemas that reflect the actual document outline.

Old behavior preserved via --mode syntactic. Validator and visualization
tools pinned to syntactic mode for compatibility.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 18:49:50 +01:00

366 lines
11 KiB
Python

"""
Tests for Issue #51: Add outline mode to schema generation
This test module implements comprehensive tests for the new outline mode functionality
that captures document structure with actual heading text and depth control.
Following TDD8 methodology - these tests are written before implementation.
"""
import json
import pytest
from pathlib import Path
from tempfile import NamedTemporaryFile
from click.testing import CliRunner
from markitect.cli import cli
from markitect.schema_generator import SchemaGenerator
from markitect.exceptions import InvalidDepthError
class TestIssue51OutlineMode:
"""Test suite for outline mode schema generation functionality."""
def setup_method(self):
"""Set up test fixtures."""
self.schema_generator = SchemaGenerator()
self.runner = CliRunner()
def test_cli_accepts_mode_outline_option(self):
"""Test that CLI accepts --mode outline option."""
# Arrange
markdown_content = """# Test Document
## Introduction
This is a test document.
### Details
Some details here.
"""
with NamedTemporaryFile(mode='w', suffix='.md', delete=False) as f:
f.write(markdown_content)
temp_file = Path(f.name)
try:
# Act
result = self.runner.invoke(cli, [
'schema-generate',
'--mode', 'outline',
str(temp_file)
])
# Assert
assert result.exit_code == 0, f"CLI should accept --mode outline option, got: {result.output}"
finally:
temp_file.unlink()
def test_cli_accepts_depth_parameter(self):
"""Test that CLI accepts --depth parameter with outline mode."""
# Arrange
markdown_content = """# Test Document
## Introduction
This is a test document.
### Details
Some details here.
#### Specifics
Very specific information.
"""
with NamedTemporaryFile(mode='w', suffix='.md', delete=False) as f:
f.write(markdown_content)
temp_file = Path(f.name)
try:
# Act
result = self.runner.invoke(cli, [
'schema-generate',
'--mode', 'outline',
'--depth', '2',
str(temp_file)
])
# Assert
assert result.exit_code == 0, f"CLI should accept --depth parameter, got: {result.output}"
finally:
temp_file.unlink()
def test_outline_mode_generates_schema_with_from_title(self):
"""Test that outline mode generates schema with 'from' in title instead of 'for'."""
# Arrange
markdown_content = """# Test Document
## Introduction
This is a test document.
"""
with NamedTemporaryFile(mode='w', suffix='.md', delete=False) as f:
f.write(markdown_content)
temp_file = Path(f.name)
try:
# Act
result = self.runner.invoke(cli, [
'schema-generate',
'--mode', 'outline',
str(temp_file)
])
# Assert
assert result.exit_code == 0
schema = json.loads(result.output)
expected_title = f"Schema from {temp_file.name}"
assert schema["title"] == expected_title, f"Expected title 'Schema from {temp_file.name}', got '{schema.get('title')}'"
finally:
temp_file.unlink()
def test_outline_mode_captures_actual_heading_text(self):
"""Test that outline mode captures actual heading text in schema."""
# Arrange
markdown_content = """# Main Architecture Document
## System Overview
High-level system description.
### Core Components
Details about main components.
## Implementation Strategy
Strategy for implementation.
"""
with NamedTemporaryFile(mode='w', suffix='.md', delete=False) as f:
f.write(markdown_content)
temp_file = Path(f.name)
try:
# Act
result = self.runner.invoke(cli, [
'schema-generate',
'--mode', 'outline',
str(temp_file)
])
# Assert
assert result.exit_code == 0
schema = json.loads(result.output)
# Check that headings properties exist and contain actual text
assert "headings" in schema["properties"], "Schema should contain headings property"
# Should have level_1, level_2, level_3 based on content
headings = schema["properties"]["headings"]["properties"]
assert "level_1" in headings, "Should have level_1 headings"
assert "level_2" in headings, "Should have level_2 headings"
assert "level_3" in headings, "Should have level_3 headings"
# Check heading text is captured (this will need to be implemented)
# For now, verify structure exists
level_1_schema = headings["level_1"]
assert level_1_schema["type"] == "array"
assert "items" in level_1_schema
finally:
temp_file.unlink()
def test_outline_mode_with_depth_limit_respects_depth(self):
"""Test that outline mode with --depth parameter respects depth limit."""
# Arrange
markdown_content = """# Main Document
## Section A
Content A.
### Subsection A1
Content A1.
#### Deep Section A1.1
Very deep content.
## Section B
Content B.
"""
with NamedTemporaryFile(mode='w', suffix='.md', delete=False) as f:
f.write(markdown_content)
temp_file = Path(f.name)
try:
# Act
result = self.runner.invoke(cli, [
'schema-generate',
'--mode', 'outline',
'--depth', '2',
str(temp_file)
])
# Assert
assert result.exit_code == 0
schema = json.loads(result.output)
headings = schema["properties"]["headings"]["properties"]
assert "level_1" in headings, "Should have level_1 headings"
assert "level_2" in headings, "Should have level_2 headings"
assert "level_3" not in headings, "Should not have level_3 headings with depth=2"
assert "level_4" not in headings, "Should not have level_4 headings with depth=2"
finally:
temp_file.unlink()
def test_outline_mode_integrates_with_metaschema_extensions(self):
"""Test that outline mode integrates with metaschema extensions from Issue #50."""
# Arrange
markdown_content = """# Test Document
## Introduction
This is a test document.
"""
with NamedTemporaryFile(mode='w', suffix='.md', delete=False) as f:
f.write(markdown_content)
temp_file = Path(f.name)
try:
# Act
result = self.runner.invoke(cli, [
'schema-generate',
'--mode', 'outline',
'--depth', '3',
str(temp_file)
])
# Assert
assert result.exit_code == 0
schema = json.loads(result.output)
# Check for metaschema extensions
assert "x-markitect-outline-mode" in schema, "Should have outline mode marker"
assert schema["x-markitect-outline-mode"] is True, "Outline mode should be marked as true"
assert "x-markitect-outline-depth" in schema, "Should have outline depth marker"
assert schema["x-markitect-outline-depth"] == 3, "Should record the depth setting"
finally:
temp_file.unlink()
def test_outline_mode_works_with_outfile_parameter(self):
"""Test that outline mode works with existing --outfile parameter."""
# Arrange
markdown_content = """# Test Document
## Introduction
This is a test document.
"""
with NamedTemporaryFile(mode='w', suffix='.md', delete=False) as f:
f.write(markdown_content)
temp_file = Path(f.name)
with NamedTemporaryFile(mode='w', suffix='.json', delete=False) as outf:
output_file = Path(outf.name)
try:
# Act
result = self.runner.invoke(cli, [
'schema-generate',
'--mode', 'outline',
'--outfile', str(output_file),
str(temp_file)
])
# Assert
assert result.exit_code == 0
assert output_file.exists(), "Output file should be created"
schema_content = output_file.read_text()
schema = json.loads(schema_content)
expected_title = f"Schema from {temp_file.name}"
assert schema["title"] == expected_title
finally:
temp_file.unlink()
if output_file.exists():
output_file.unlink()
def test_cli_maintains_backward_compatibility_with_max_depth(self):
"""Test that existing --max-depth option still works with default (semantic) mode."""
# Arrange
markdown_content = """# Test Document
## Introduction
This is a test document.
### Details
Some details here.
"""
with NamedTemporaryFile(mode='w', suffix='.md', delete=False) as f:
f.write(markdown_content)
temp_file = Path(f.name)
try:
# Act
result = self.runner.invoke(cli, [
'schema-generate',
'--max-depth', '2',
str(temp_file)
])
# Assert
assert result.exit_code == 0, f"CLI should maintain backward compatibility with --max-depth, got: {result.output}"
schema = json.loads(result.output)
# Default mode is now semantic, which uses 'from' in title
expected_title = f"Schema from {temp_file.name}"
assert schema["title"] == expected_title, f"Default (semantic) mode should use 'from' in title"
finally:
temp_file.unlink()
def test_depth_parameter_validation(self):
"""Test that --depth parameter validates input correctly."""
# Arrange
markdown_content = """# Test Document
## Introduction
This is a test document.
"""
with NamedTemporaryFile(mode='w', suffix='.md', delete=False) as f:
f.write(markdown_content)
temp_file = Path(f.name)
try:
# Act - Test invalid depth
result = self.runner.invoke(cli, [
'schema-generate',
'--mode', 'outline',
'--depth', '0',
str(temp_file)
])
# Assert
assert result.exit_code != 0, "Should reject depth=0"
assert "Invalid depth parameter" in result.output or "depth must be >= 1" in result.output
finally:
temp_file.unlink()
def test_cli_help_includes_new_options(self):
"""Test that CLI help text includes documentation for new options."""
# Act
result = self.runner.invoke(cli, ['schema-generate', '--help'])
# Assert
assert result.exit_code == 0
help_text = result.output
assert "--mode" in help_text, "Help should document --mode option"
assert "--depth" in help_text, "Help should document --depth option"
assert "outline" in help_text, "Help should mention outline mode"