13618fb8d5
CI / benchmark-publish (pull_request) Has been skipped
CI / lint (pull_request) Successful in 14s
CI / build (pull_request) Successful in 15s
CI / quality (pull_request) Successful in 17s
CI / security (pull_request) Successful in 33s
CI / typecheck (pull_request) Successful in 47s
CI / unit_tests (pull_request) Successful in 2m35s
CI / integration_tests (pull_request) Successful in 3m13s
CI / docker (pull_request) Successful in 39s
CI / coverage (pull_request) Successful in 4m26s
CI / benchmark-regression (pull_request) Has been cancelled
- F1/F2 [P1]: Split postgresql_analyzer.py (695→310 lines) by
extracting regex patterns, URI builders, and parsing functions
into _postgresql_helpers.py (440 lines). Split
domain_analyzers.feature (605→217 lines) into separate
postgresql_analyzer.feature (201) and
docker_compose_analyzer.feature (199).
- F3 [P1]: Fix _extract_body and _split_entries to track
single-quoted SQL string literals so parentheses inside
DEFAULT/CHECK values (e.g. 'func(x)') do not corrupt depth
counting. Added regression scenarios.
- F4 [P2]: Remove prohibited `# type: ignore[arg-type]` by
adding a static protocol assertion for _DuplicatePyAnalyzer.
- F5 [P2]: Add 1 MiB size guard before yaml.safe_load in
DockerComposeAnalyzer to mitigate billion-laughs alias
expansion attacks.
- F6 [P2]: Fix quoted-keyword column names (e.g. "primary")
being silently dropped by checking raw_first.startswith('"')
before keyword filtering. Added regression scenario.
- F7 [P2]: Out-of-scope file changes resolved by rebase onto
master (changes already merged via other PRs).
All nox gates pass: lint (0 errors), typecheck (0 errors),
security_scan, behave (57 scenarios, 211 steps, 0 failures).
218 lines
8.1 KiB
Gherkin
218 lines
8.1 KiB
Gherkin
@phase2 @acms @analyzer
|
|
Feature: Domain-Specific Analyzers
|
|
As a CleverAgents developer
|
|
I want domain-specific analyzers that produce UKO triples from resources
|
|
So that the ACMS can index code, documents, SQL, and infrastructure into the UKO knowledge graph
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# AnalyzerProtocol conformance
|
|
# ---------------------------------------------------------------------------
|
|
|
|
@analyzer_protocol
|
|
Scenario: PythonAnalyzer satisfies AnalyzerProtocol
|
|
Given a PythonAnalyzer analyzer
|
|
Then the analyzer should satisfy the AnalyzerProtocol
|
|
And the current analyzer domain should be "python"
|
|
|
|
@analyzer_protocol
|
|
Scenario: MarkdownAnalyzer satisfies AnalyzerProtocol
|
|
Given a MarkdownAnalyzer analyzer
|
|
Then the analyzer should satisfy the AnalyzerProtocol
|
|
And the current analyzer domain should be "markdown"
|
|
|
|
@analyzer_protocol
|
|
Scenario: PostgreSQLAnalyzer satisfies AnalyzerProtocol
|
|
Given a PostgreSQLAnalyzer analyzer
|
|
Then the analyzer should satisfy the AnalyzerProtocol
|
|
And the current analyzer domain should be "postgresql"
|
|
|
|
@analyzer_protocol
|
|
Scenario: DockerComposeAnalyzer satisfies AnalyzerProtocol
|
|
Given a DockerComposeAnalyzer analyzer
|
|
Then the analyzer should satisfy the AnalyzerProtocol
|
|
And the current analyzer domain should be "docker-compose"
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# AnalyzerRegistry
|
|
# ---------------------------------------------------------------------------
|
|
|
|
@analyzer_registry
|
|
Scenario: Register all 4 analyzers and verify count
|
|
Given a fresh analyzer registry
|
|
When I register all 4 domain analyzers
|
|
Then the registry should contain 4 analyzers
|
|
|
|
@analyzer_registry
|
|
Scenario: Lookup by extension returns correct analyzer for .py
|
|
Given a fresh analyzer registry with all domain analyzers registered
|
|
When I look up extension ".py" in the registry
|
|
Then the current analyzer domain should be "python"
|
|
|
|
@analyzer_registry
|
|
Scenario: Lookup by extension returns correct analyzer for .sql
|
|
Given a fresh analyzer registry with all domain analyzers registered
|
|
When I look up extension ".sql" in the registry
|
|
Then the current analyzer domain should be "postgresql"
|
|
|
|
@analyzer_registry
|
|
Scenario: Lookup for unknown extension returns None
|
|
Given a fresh analyzer registry with all domain analyzers registered
|
|
When I look up extension ".rs" in the registry
|
|
Then the extension lookup result should be None
|
|
|
|
@analyzer_registry
|
|
Scenario: Register duplicate extension first wins
|
|
Given a fresh analyzer registry
|
|
When I register a PythonAnalyzer
|
|
And I register a duplicate analyzer that also claims ".py"
|
|
And I look up extension ".py" in the registry
|
|
Then the current analyzer domain should be "python"
|
|
|
|
@analyzer_registry
|
|
Scenario: list_extensions returns all registered extensions
|
|
Given a fresh analyzer registry with all domain analyzers registered
|
|
Then the registry extensions should include ".py"
|
|
And the registry extensions should include ".pyi"
|
|
And the registry extensions should include ".md"
|
|
And the registry extensions should include ".markdown"
|
|
And the registry extensions should include ".sql"
|
|
And the registry extensions should include ".ddl"
|
|
And the registry extensions should include ".yml"
|
|
And the registry extensions should include ".yaml"
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# PythonAnalyzer
|
|
# ---------------------------------------------------------------------------
|
|
|
|
@python_analyzer
|
|
Scenario: Analyze Python class definition
|
|
Given a PythonAnalyzer analyzer
|
|
When I analyze the Python content:
|
|
"""
|
|
class MyService:
|
|
pass
|
|
"""
|
|
Then the triples should include predicate "rdf:type" with object_uri "uko-code:Module"
|
|
And the triples should include predicate "rdf:type" with object_uri "uko-py:Class"
|
|
And the triples should include predicate "uko:contains" linking subject "module" to object "MyService"
|
|
|
|
@python_analyzer
|
|
Scenario: Analyze Python function with docstring
|
|
Given a PythonAnalyzer analyzer
|
|
When I analyze the Python content:
|
|
"""
|
|
def process_data():
|
|
'''Process incoming data.'''
|
|
pass
|
|
"""
|
|
Then the triples should include predicate "rdf:type" with object_uri "uko-py:Function"
|
|
And the triples should include predicate "uko-doc:hasDocstring"
|
|
|
|
@python_analyzer
|
|
Scenario: Analyze Python imports
|
|
Given a PythonAnalyzer analyzer
|
|
When I analyze the Python content:
|
|
"""
|
|
import os
|
|
from pathlib import Path
|
|
"""
|
|
Then the triples should include predicate "uko:references" with object_uri containing "os"
|
|
And the triples should include predicate "uko:references" with object_uri containing "pathlib"
|
|
|
|
@python_analyzer
|
|
Scenario: Analyze empty Python content raises ValueError
|
|
Given a PythonAnalyzer analyzer
|
|
Then analyzing empty content with this analyzer should raise ValueError
|
|
|
|
@python_analyzer
|
|
Scenario: Analyze invalid Python syntax returns empty list
|
|
Given a PythonAnalyzer analyzer
|
|
When I analyze the Python content:
|
|
"""
|
|
def broken(
|
|
pass
|
|
class ???
|
|
"""
|
|
Then no triples should be returned
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# MarkdownAnalyzer
|
|
# ---------------------------------------------------------------------------
|
|
|
|
@markdown_analyzer
|
|
Scenario: Analyze heading hierarchy
|
|
Given a MarkdownAnalyzer analyzer
|
|
When I analyze the Markdown content:
|
|
"""
|
|
# Introduction
|
|
Some text.
|
|
## Details
|
|
More text.
|
|
"""
|
|
Then the triples should include predicate "rdf:type" with object_uri "uko-doc:Document"
|
|
And the triples should include predicate "rdf:type" with object_uri "uko-doc:Section"
|
|
And the triples should include predicate "uko-doc:headingLevel" with object_value "1"
|
|
And the triples should include predicate "uko-doc:headingLevel" with object_value "2"
|
|
|
|
@markdown_analyzer
|
|
Scenario: Analyze code block with language
|
|
Given a MarkdownAnalyzer analyzer
|
|
When I analyze the Markdown content:
|
|
"""
|
|
# Example
|
|
```python
|
|
print("hello")
|
|
```
|
|
"""
|
|
Then the triples should include predicate "rdf:type" with object_uri "uko-code:CodeBlock"
|
|
And the triples should include predicate "uko-code:language" with object_value "python"
|
|
|
|
@markdown_analyzer
|
|
Scenario: Analyze links
|
|
Given a MarkdownAnalyzer analyzer
|
|
When I analyze the Markdown content:
|
|
"""
|
|
# Resources
|
|
See [Example](https://example.com) for details.
|
|
"""
|
|
Then the triples should include predicate "uko:references" with object_value "https://example.com"
|
|
|
|
@markdown_analyzer
|
|
Scenario: Analyze empty Markdown content raises ValueError
|
|
Given a MarkdownAnalyzer analyzer
|
|
Then analyzing empty content with this analyzer should raise ValueError
|
|
|
|
@markdown_analyzer
|
|
Scenario: Analyze content with no headings still produces Document triple
|
|
Given a MarkdownAnalyzer analyzer
|
|
When I analyze the Markdown content:
|
|
"""
|
|
Just some plain text without any headings.
|
|
Another line of text.
|
|
"""
|
|
Then the triples should include predicate "rdf:type" with object_uri "uko-doc:Document"
|
|
And the triples should not include predicate "rdf:type" with object_uri "uko-doc:Section"
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Cross-analyzer
|
|
# ---------------------------------------------------------------------------
|
|
|
|
@analyzer_cross
|
|
Scenario: All analyzers use UKO URI scheme
|
|
Given a PythonAnalyzer analyzer
|
|
And a MarkdownAnalyzer analyzer
|
|
And a PostgreSQLAnalyzer analyzer
|
|
And a DockerComposeAnalyzer analyzer
|
|
When each analyzer processes its sample content
|
|
Then every triple subject_uri should start with "uko://"
|
|
|
|
@analyzer_cross
|
|
Scenario: Confidence scores default to 1.0
|
|
Given a PythonAnalyzer analyzer
|
|
When I analyze the Python content:
|
|
"""
|
|
class Example:
|
|
pass
|
|
"""
|
|
Then every triple confidence should be 1.0
|