Files
markitect-main/tests/test_issue_54_content_instructions.py
tegwick a8d9b9289c test: Add comprehensive test suite for Issue #54 content instructions
This commit adds the complete test suite for content field instruction
capabilities, providing comprehensive coverage for all implemented features.

## Test Coverage:
- CLI option validation (--include-content-instructions, --instruction-type)
- Schema generation with content instruction fields
- Integration with outline mode and heading text capture
- Backward compatibility verification
- Error handling for invalid instruction types
- Stub generator integration
- Content instruction text generation for all types

## Test Structure:
- 13 comprehensive test methods covering all use cases
- TDD methodology validation (RED-GREEN-REFACTOR cycle)
- Integration tests for feature combinations
- Edge case and error condition testing

This completes the test coverage for Issue #54 implementation.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-01 08:24:39 +02:00

515 lines
17 KiB
Python

"""
Tests for Issue #54: Add content field instruction capabilities
This test module implements comprehensive tests for content field instructions
that provide guidance for content authors during document generation.
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.stub_generator import StubGenerator
from markitect.exceptions import InvalidInstructionTypeError
class TestIssue54ContentInstructions:
"""Test suite for content field instruction functionality."""
def setup_method(self):
"""Set up test fixtures."""
self.schema_generator = SchemaGenerator()
self.stub_generator = StubGenerator()
self.runner = CliRunner()
def test_cli_accepts_include_content_instructions_option(self):
"""Test that CLI accepts --include-content-instructions option."""
# Arrange
markdown_content = """# Architecture Document
## Introduction
This section provides an overview of the system.
## Design Principles
Core principles guiding the design.
"""
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',
'--include-content-instructions',
str(temp_file)
])
# Assert
assert result.exit_code == 0, f"CLI should accept --include-content-instructions option, got: {result.output}"
finally:
temp_file.unlink()
def test_schema_generation_with_content_instructions_includes_instruction_fields(self):
"""Test that schema generation with content instructions includes instruction fields."""
# Arrange
markdown_content = """# Software Architecture Document
## Introduction
This section provides an overview of the system architecture.
### Purpose
Explain the purpose and goals of this document.
## System Design
Describe the overall system design and architecture.
"""
with NamedTemporaryFile(mode='w', suffix='.md', delete=False) as f:
f.write(markdown_content)
temp_file = Path(f.name)
try:
# Act
schema = self.schema_generator.generate_schema_from_file(
temp_file,
include_content_instructions=True
)
# Assert - Schema should contain content instruction fields
assert "properties" in schema
assert "headings" in schema["properties"]
headings = schema["properties"]["headings"]["properties"]
# Level 1 heading should have content instructions
level_1 = headings["level_1"]
items_props = level_1["items"]["properties"]
assert "x-markitect-content-instructions" in items_props
assert items_props["x-markitect-content-instructions"]["type"] == "string"
# Level 2 headings should have content instructions
level_2 = headings["level_2"]
items_props = level_2["items"]["properties"]
assert "x-markitect-content-instructions" in items_props
finally:
temp_file.unlink()
def test_schema_includes_content_instruction_metaschema_extension(self):
"""Test that schemas with content instructions include metaschema extension."""
# Arrange
markdown_content = """# Test Document
## Section A
Content for section A.
"""
with NamedTemporaryFile(mode='w', suffix='.md', delete=False) as f:
f.write(markdown_content)
temp_file = Path(f.name)
try:
# Act
schema = self.schema_generator.generate_schema_from_file(
temp_file,
include_content_instructions=True
)
# Assert - Should have metaschema extension
assert "x-markitect-content-instructions-enabled" in schema
assert schema["x-markitect-content-instructions-enabled"] is True
finally:
temp_file.unlink()
def test_content_instructions_support_different_instruction_types(self):
"""Test that content instructions can specify different instruction types."""
# Arrange
markdown_content = """# Requirements Document
## Functional Requirements
List all functional requirements.
## Non-Functional Requirements
Describe performance, security, and usability requirements.
"""
with NamedTemporaryFile(mode='w', suffix='.md', delete=False) as f:
f.write(markdown_content)
temp_file = Path(f.name)
try:
# Act
schema = self.schema_generator.generate_schema_from_file(
temp_file,
include_content_instructions=True,
instruction_type="description"
)
# Assert - Should have instruction type specified
headings = schema["properties"]["headings"]["properties"]
level_2 = headings["level_2"]
items_props = level_2["items"]["properties"]
assert "x-markitect-instruction-type" in items_props
assert items_props["x-markitect-instruction-type"]["enum"] == ["description"]
finally:
temp_file.unlink()
def test_content_instructions_integration_with_outline_mode(self):
"""Test that content instructions work with outline mode."""
# Arrange
markdown_content = """# Project Plan
## Phase 1: Planning
Planning activities and deliverables.
### Requirements Gathering
Gather and document all requirements.
### Architecture Design
Design the system architecture.
## Phase 2: Implementation
Implementation activities.
"""
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',
'--include-content-instructions',
'--depth', '2',
str(temp_file)
])
# Assert
assert result.exit_code == 0
schema = json.loads(result.output)
# Should have both outline mode and content instructions extensions
assert schema.get("x-markitect-outline-mode") is True
assert schema.get("x-markitect-content-instructions-enabled") is True
# Should only include headings up to depth 2
headings = schema["properties"]["headings"]["properties"]
assert "level_1" in headings
assert "level_2" in headings
assert "level_3" not in headings
# Should have content instructions in the headings
level_2 = headings["level_2"]
items_props = level_2["items"]["properties"]
assert "x-markitect-content-instructions" in items_props
finally:
temp_file.unlink()
def test_content_instructions_for_paragraphs_and_lists(self):
"""Test that content instructions can be added for paragraphs and lists."""
# Arrange
markdown_content = """# User Guide
## Overview
This guide explains how to use the system.
Some introductory text here.
- Feature A
- Feature B
- Feature C
## Getting Started
Follow these steps to get started.
"""
with NamedTemporaryFile(mode='w', suffix='.md', delete=False) as f:
f.write(markdown_content)
temp_file = Path(f.name)
try:
# Act
schema = self.schema_generator.generate_schema_from_file(
temp_file,
include_content_instructions=True
)
# Assert - Paragraphs should have content instructions
if "paragraphs" in schema["properties"]:
paragraphs_schema = schema["properties"]["paragraphs"]
items_props = paragraphs_schema["items"]["properties"]
assert "x-markitect-content-instructions" in items_props
# Lists should have content instructions
if "lists" in schema["properties"]:
lists_schema = schema["properties"]["lists"]
items_props = lists_schema["items"]["properties"]
assert "x-markitect-content-instructions" in items_props
finally:
temp_file.unlink()
def test_stub_generation_includes_content_instruction_placeholders(self):
"""Test that stub generation includes content instruction placeholders."""
# Arrange
schema = {
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"title": "Test Schema",
"x-markitect-content-instructions-enabled": True,
"properties": {
"headings": {
"type": "object",
"properties": {
"level_1": {
"type": "array",
"items": {
"type": "object",
"properties": {
"content": {"type": "string"},
"x-markitect-content-instructions": {
"type": "string",
"const": "Provide the main title of the document"
}
}
}
},
"level_2": {
"type": "array",
"items": {
"type": "object",
"properties": {
"content": {"type": "string"},
"x-markitect-content-instructions": {
"type": "string",
"const": "Describe each major section of the document"
}
}
}
}
}
}
}
}
# Act
stub_content = self.stub_generator.generate_stub_from_schema(schema)
# Assert - Stub should include instruction placeholders
assert "Provide the main title of the document" in stub_content
assert "Describe each major section of the document" in stub_content
def test_cli_supports_instruction_type_parameter(self):
"""Test that CLI supports --instruction-type parameter."""
# Arrange
markdown_content = """# API Documentation
## Authentication
How to authenticate with the API.
## Endpoints
Available API endpoints.
"""
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',
'--include-content-instructions',
'--instruction-type', 'example',
str(temp_file)
])
# Assert
assert result.exit_code == 0
schema = json.loads(result.output)
# Check that instruction type is set correctly
headings = schema["properties"]["headings"]["properties"]
level_1 = headings["level_1"]
items_props = level_1["items"]["properties"]
assert items_props["x-markitect-instruction-type"]["enum"] == ["example"]
finally:
temp_file.unlink()
def test_backward_compatibility_without_content_instructions(self):
"""Test that existing behavior is maintained when content instructions are not enabled."""
# Arrange
markdown_content = """# Test Document
## Section One
Content here.
"""
with NamedTemporaryFile(mode='w', suffix='.md', delete=False) as f:
f.write(markdown_content)
temp_file = Path(f.name)
try:
# Act - Generate schema without content instructions (default behavior)
schema = self.schema_generator.generate_schema_from_file(temp_file)
# Assert - Should NOT have content instruction fields
headings = schema["properties"]["headings"]["properties"]
level_1 = headings["level_1"]
items_props = level_1["items"]["properties"]
# Should not have content instruction fields
assert "x-markitect-content-instructions" not in items_props
assert "x-markitect-instruction-type" not in items_props
# Should NOT have content instructions extension
assert "x-markitect-content-instructions-enabled" not in schema
finally:
temp_file.unlink()
def test_content_instructions_with_heading_text_capture_integration(self):
"""Test that content instructions work with heading text capture."""
# Arrange
markdown_content = """# Architecture Overview
## System Components
Core system components and their responsibilities.
## Data Flow
How data flows through the system.
"""
with NamedTemporaryFile(mode='w', suffix='.md', delete=False) as f:
f.write(markdown_content)
temp_file = Path(f.name)
try:
# Act
schema = self.schema_generator.generate_schema_from_file(
temp_file,
capture_heading_text=True,
include_content_instructions=True
)
# Assert - Should have both heading text capture and content instructions
assert schema.get("x-markitect-heading-text-capture") is True
assert schema.get("x-markitect-content-instructions-enabled") is True
# Headings should have both enum constraints and content instructions
headings = schema["properties"]["headings"]["properties"]
level_1 = headings["level_1"]
items_props = level_1["items"]["properties"]
# Should have enum constraint from heading text capture
assert "enum" in items_props["content"]
assert items_props["content"]["enum"] == ["Architecture Overview"]
# Should also have content instructions
assert "x-markitect-content-instructions" in items_props
finally:
temp_file.unlink()
def test_cli_help_includes_content_instructions_options(self):
"""Test that CLI help includes documentation for content instruction options."""
# Act
result = self.runner.invoke(cli, ['schema-generate', '--help'])
# Assert
assert result.exit_code == 0
help_text = result.output
assert "--include-content-instructions" in help_text
assert "--instruction-type" in help_text
assert "content instructions" in help_text or "content guidance" in help_text
def test_instruction_type_validation(self):
"""Test that --instruction-type parameter validates input correctly."""
# Arrange
markdown_content = """# Test Document
## Section
Content here.
"""
with NamedTemporaryFile(mode='w', suffix='.md', delete=False) as f:
f.write(markdown_content)
temp_file = Path(f.name)
try:
# Act - Test invalid instruction type
result = self.runner.invoke(cli, [
'schema-generate',
'--include-content-instructions',
'--instruction-type', 'invalid-type',
str(temp_file)
])
# Assert
assert result.exit_code != 0
assert "Invalid instruction type" in result.output or "invalid-type" in result.output
finally:
temp_file.unlink()
def test_content_instructions_generate_appropriate_default_text(self):
"""Test that content instructions generate appropriate default guidance text."""
# Arrange
markdown_content = """# Development Guide
## Prerequisites
System requirements and prerequisites.
### Software Requirements
Required software and versions.
## Installation
Step-by-step installation instructions.
## Configuration
How to configure the system.
"""
with NamedTemporaryFile(mode='w', suffix='.md', delete=False) as f:
f.write(markdown_content)
temp_file = Path(f.name)
try:
# Act
schema = self.schema_generator.generate_schema_from_file(
temp_file,
include_content_instructions=True,
instruction_type="description"
)
# Assert - Content instructions should contain appropriate guidance
headings = schema["properties"]["headings"]["properties"]
# Level 1 should have appropriate instructions
level_1 = headings["level_1"]
items_props = level_1["items"]["properties"]
instructions = items_props["x-markitect-content-instructions"]["const"]
assert len(instructions) > 0
assert isinstance(instructions, str)
# Instructions should be contextually appropriate
# (implementation will determine specific text)
finally:
temp_file.unlink()