Navegador

fix: tighten framework detection — query Import nodes, not substring matches detect() now checks Import nodes by exact module name (n.name = $name OR n.module = $name) instead of CONTAINS on all nodes. Added detection_files for marker file matching by exact File node name. Eliminates false positives: navegador's own codebase now correctly reports zero framework detections instead of falsely matching FastAPI and Laravel. Bumps to 0.7.4.

lmata 2026-03-24 05:07 trunk
Commit 2c266d225208a3e8cfc6bed8226530ffbdd04a65ce7768011cbe08655720bff5
--- navegador/__init__.py
+++ navegador/__init__.py
@@ -1,10 +1,10 @@
11
"""
22
Navegador — AST + knowledge graph context engine for AI coding agents.
33
"""
44
5
-__version__ = "0.7.3"
5
+__version__ = "0.7.4"
66
__author__ = "CONFLICT LLC"
77
88
from navegador.sdk import Navegador
99
1010
__all__ = ["Navegador"]
1111
--- navegador/__init__.py
+++ navegador/__init__.py
@@ -1,10 +1,10 @@
1 """
2 Navegador — AST + knowledge graph context engine for AI coding agents.
3 """
4
5 __version__ = "0.7.3"
6 __author__ = "CONFLICT LLC"
7
8 from navegador.sdk import Navegador
9
10 __all__ = ["Navegador"]
11
--- navegador/__init__.py
+++ navegador/__init__.py
@@ -1,10 +1,10 @@
1 """
2 Navegador — AST + knowledge graph context engine for AI coding agents.
3 """
4
5 __version__ = "0.7.4"
6 __author__ = "CONFLICT LLC"
7
8 from navegador.sdk import Navegador
9
10 __all__ = ["Navegador"]
11
--- navegador/enrichment/base.py
+++ navegador/enrichment/base.py
@@ -32,31 +32,54 @@
3232
"""Name of the framework (e.g. 'django', 'fastapi')."""
3333
3434
@property
3535
@abstractmethod
3636
def detection_patterns(self) -> list[str]:
37
- """File/import patterns that indicate this framework is in use.
37
+ """Import module names that indicate this framework is in use.
38
+
39
+ Detection queries Import nodes for exact module matches, so use
40
+ the actual package name (e.g. 'django', 'fastapi', 'express').
41
+ """
42
+
43
+ @property
44
+ def detection_files(self) -> list[str]:
45
+ """Filenames whose presence confirms the framework.
3846
39
- E.g. ['manage.py', 'django.conf.settings'] for Django.
47
+ Override this to add file-based detection (e.g. 'manage.py' for
48
+ Django, 'Gemfile' for Rails). File names are matched exactly
49
+ against File node names — not as substrings.
4050
"""
51
+ return []
4152
4253
@abstractmethod
4354
def enrich(self) -> EnrichmentResult:
4455
"""Run enrichment on the current graph."""
4556
4657
def detect(self) -> bool:
47
- """Check if the framework is present in the graph by looking for detection patterns."""
58
+ """Check if the framework is present by looking for real imports and marker files."""
59
+ # Check Import nodes for actual framework imports
4860
for pattern in self.detection_patterns:
4961
result = self.store.query(
50
- "MATCH (n) WHERE n.name CONTAINS $pattern OR "
51
- "(n.file_path IS NOT NULL AND n.file_path CONTAINS $pattern) "
52
- "RETURN count(n) AS c LIMIT 1",
53
- {"pattern": pattern},
62
+ "MATCH (n:Import) WHERE n.name = $name OR n.module = $name "
63
+ "RETURN count(n) AS c",
64
+ {"name": pattern},
65
+ )
66
+ rows = result.result_set or []
67
+ if rows and rows[0][0] > 0:
68
+ return True
69
+
70
+ # Check for marker files by exact filename match
71
+ for filename in self.detection_files:
72
+ result = self.store.query(
73
+ "MATCH (f:File) WHERE f.name = $name "
74
+ "RETURN count(f) AS c",
75
+ {"name": filename},
5476
)
5577
rows = result.result_set or []
5678
if rows and rows[0][0] > 0:
5779
return True
80
+
5881
return False
5982
6083
def _promote_node(
6184
self, name: str, file_path: str, semantic_type: str, props: dict = None
6285
) -> None:
6386
--- navegador/enrichment/base.py
+++ navegador/enrichment/base.py
@@ -32,31 +32,54 @@
32 """Name of the framework (e.g. 'django', 'fastapi')."""
33
34 @property
35 @abstractmethod
36 def detection_patterns(self) -> list[str]:
37 """File/import patterns that indicate this framework is in use.
 
 
 
 
 
 
 
 
38
39 E.g. ['manage.py', 'django.conf.settings'] for Django.
 
 
40 """
 
41
42 @abstractmethod
43 def enrich(self) -> EnrichmentResult:
44 """Run enrichment on the current graph."""
45
46 def detect(self) -> bool:
47 """Check if the framework is present in the graph by looking for detection patterns."""
 
48 for pattern in self.detection_patterns:
49 result = self.store.query(
50 "MATCH (n) WHERE n.name CONTAINS $pattern OR "
51 "(n.file_path IS NOT NULL AND n.file_path CONTAINS $pattern) "
52 "RETURN count(n) AS c LIMIT 1",
53 {"pattern": pattern},
 
 
 
 
 
 
 
 
 
 
54 )
55 rows = result.result_set or []
56 if rows and rows[0][0] > 0:
57 return True
 
58 return False
59
60 def _promote_node(
61 self, name: str, file_path: str, semantic_type: str, props: dict = None
62 ) -> None:
63
--- navegador/enrichment/base.py
+++ navegador/enrichment/base.py
@@ -32,31 +32,54 @@
32 """Name of the framework (e.g. 'django', 'fastapi')."""
33
34 @property
35 @abstractmethod
36 def detection_patterns(self) -> list[str]:
37 """Import module names that indicate this framework is in use.
38
39 Detection queries Import nodes for exact module matches, so use
40 the actual package name (e.g. 'django', 'fastapi', 'express').
41 """
42
43 @property
44 def detection_files(self) -> list[str]:
45 """Filenames whose presence confirms the framework.
46
47 Override this to add file-based detection (e.g. 'manage.py' for
48 Django, 'Gemfile' for Rails). File names are matched exactly
49 against File node names — not as substrings.
50 """
51 return []
52
53 @abstractmethod
54 def enrich(self) -> EnrichmentResult:
55 """Run enrichment on the current graph."""
56
57 def detect(self) -> bool:
58 """Check if the framework is present by looking for real imports and marker files."""
59 # Check Import nodes for actual framework imports
60 for pattern in self.detection_patterns:
61 result = self.store.query(
62 "MATCH (n:Import) WHERE n.name = $name OR n.module = $name "
63 "RETURN count(n) AS c",
64 {"name": pattern},
65 )
66 rows = result.result_set or []
67 if rows and rows[0][0] > 0:
68 return True
69
70 # Check for marker files by exact filename match
71 for filename in self.detection_files:
72 result = self.store.query(
73 "MATCH (f:File) WHERE f.name = $name "
74 "RETURN count(f) AS c",
75 {"name": filename},
76 )
77 rows = result.result_set or []
78 if rows and rows[0][0] > 0:
79 return True
80
81 return False
82
83 def _promote_node(
84 self, name: str, file_path: str, semantic_type: str, props: dict = None
85 ) -> None:
86
--- navegador/enrichment/django.py
+++ navegador/enrichment/django.py
@@ -20,11 +20,15 @@
2020
def framework_name(self) -> str:
2121
return "django"
2222
2323
@property
2424
def detection_patterns(self) -> list[str]:
25
- return ["manage.py", "django.conf", "settings.py", "urls.py"]
25
+ return ["django", "django.conf", "django.db", "django.http"]
26
+
27
+ @property
28
+ def detection_files(self) -> list[str]:
29
+ return ["manage.py"]
2630
2731
def enrich(self) -> EnrichmentResult:
2832
result = EnrichmentResult()
2933
3034
# ── Views ────────────────────────────────────────────────────────────
3135
--- navegador/enrichment/django.py
+++ navegador/enrichment/django.py
@@ -20,11 +20,15 @@
20 def framework_name(self) -> str:
21 return "django"
22
23 @property
24 def detection_patterns(self) -> list[str]:
25 return ["manage.py", "django.conf", "settings.py", "urls.py"]
 
 
 
 
26
27 def enrich(self) -> EnrichmentResult:
28 result = EnrichmentResult()
29
30 # ── Views ────────────────────────────────────────────────────────────
31
--- navegador/enrichment/django.py
+++ navegador/enrichment/django.py
@@ -20,11 +20,15 @@
20 def framework_name(self) -> str:
21 return "django"
22
23 @property
24 def detection_patterns(self) -> list[str]:
25 return ["django", "django.conf", "django.db", "django.http"]
26
27 @property
28 def detection_files(self) -> list[str]:
29 return ["manage.py"]
30
31 def enrich(self) -> EnrichmentResult:
32 result = EnrichmentResult()
33
34 # ── Views ────────────────────────────────────────────────────────────
35
--- navegador/enrichment/express.py
+++ navegador/enrichment/express.py
@@ -32,11 +32,11 @@
3232
def framework_name(self) -> str:
3333
return "express"
3434
3535
@property
3636
def detection_patterns(self) -> list[str]:
37
- return ["express", "Express", "app.listen"]
37
+ return ["express"]
3838
3939
def enrich(self) -> EnrichmentResult:
4040
result = EnrichmentResult()
4141
4242
# ── Routes: app.<method> or router.<method> patterns ─────────────────
4343
--- navegador/enrichment/express.py
+++ navegador/enrichment/express.py
@@ -32,11 +32,11 @@
32 def framework_name(self) -> str:
33 return "express"
34
35 @property
36 def detection_patterns(self) -> list[str]:
37 return ["express", "Express", "app.listen"]
38
39 def enrich(self) -> EnrichmentResult:
40 result = EnrichmentResult()
41
42 # ── Routes: app.<method> or router.<method> patterns ─────────────────
43
--- navegador/enrichment/express.py
+++ navegador/enrichment/express.py
@@ -32,11 +32,11 @@
32 def framework_name(self) -> str:
33 return "express"
34
35 @property
36 def detection_patterns(self) -> list[str]:
37 return ["express"]
38
39 def enrich(self) -> EnrichmentResult:
40 result = EnrichmentResult()
41
42 # ── Routes: app.<method> or router.<method> patterns ─────────────────
43
--- navegador/enrichment/fastapi.py
+++ navegador/enrichment/fastapi.py
@@ -27,11 +27,11 @@
2727
def framework_name(self) -> str:
2828
return "fastapi"
2929
3030
@property
3131
def detection_patterns(self) -> list[str]:
32
- return ["fastapi", "FastAPI", "APIRouter"]
32
+ return ["fastapi"]
3333
3434
# ── Enrichment ────────────────────────────────────────────────────────────
3535
3636
def enrich(self) -> EnrichmentResult:
3737
result = EnrichmentResult()
3838
--- navegador/enrichment/fastapi.py
+++ navegador/enrichment/fastapi.py
@@ -27,11 +27,11 @@
27 def framework_name(self) -> str:
28 return "fastapi"
29
30 @property
31 def detection_patterns(self) -> list[str]:
32 return ["fastapi", "FastAPI", "APIRouter"]
33
34 # ── Enrichment ────────────────────────────────────────────────────────────
35
36 def enrich(self) -> EnrichmentResult:
37 result = EnrichmentResult()
38
--- navegador/enrichment/fastapi.py
+++ navegador/enrichment/fastapi.py
@@ -27,11 +27,11 @@
27 def framework_name(self) -> str:
28 return "fastapi"
29
30 @property
31 def detection_patterns(self) -> list[str]:
32 return ["fastapi"]
33
34 # ── Enrichment ────────────────────────────────────────────────────────────
35
36 def enrich(self) -> EnrichmentResult:
37 result = EnrichmentResult()
38
--- navegador/enrichment/laravel.py
+++ navegador/enrichment/laravel.py
@@ -19,11 +19,15 @@
1919
def framework_name(self) -> str:
2020
return "laravel"
2121
2222
@property
2323
def detection_patterns(self) -> list[str]:
24
- return ["artisan", "Illuminate", "app/Http/Controllers"]
24
+ return ["Illuminate"]
25
+
26
+ @property
27
+ def detection_files(self) -> list[str]:
28
+ return ["artisan"]
2529
2630
def enrich(self) -> EnrichmentResult:
2731
result = EnrichmentResult()
2832
2933
# Path-based promotions: (file_path_fragment, semantic_type, pattern_key)
3034
--- navegador/enrichment/laravel.py
+++ navegador/enrichment/laravel.py
@@ -19,11 +19,15 @@
19 def framework_name(self) -> str:
20 return "laravel"
21
22 @property
23 def detection_patterns(self) -> list[str]:
24 return ["artisan", "Illuminate", "app/Http/Controllers"]
 
 
 
 
25
26 def enrich(self) -> EnrichmentResult:
27 result = EnrichmentResult()
28
29 # Path-based promotions: (file_path_fragment, semantic_type, pattern_key)
30
--- navegador/enrichment/laravel.py
+++ navegador/enrichment/laravel.py
@@ -19,11 +19,15 @@
19 def framework_name(self) -> str:
20 return "laravel"
21
22 @property
23 def detection_patterns(self) -> list[str]:
24 return ["Illuminate"]
25
26 @property
27 def detection_files(self) -> list[str]:
28 return ["artisan"]
29
30 def enrich(self) -> EnrichmentResult:
31 result = EnrichmentResult()
32
33 # Path-based promotions: (file_path_fragment, semantic_type, pattern_key)
34
--- navegador/enrichment/rails.py
+++ navegador/enrichment/rails.py
@@ -19,11 +19,15 @@
1919
def framework_name(self) -> str:
2020
return "rails"
2121
2222
@property
2323
def detection_patterns(self) -> list[str]:
24
- return ["Gemfile", "config/routes.rb", "ApplicationController", "ActiveRecord"]
24
+ return ["rails", "active_record", "action_controller"]
25
+
26
+ @property
27
+ def detection_files(self) -> list[str]:
28
+ return ["Gemfile"]
2529
2630
def enrich(self) -> EnrichmentResult:
2731
result = EnrichmentResult()
2832
2933
# Each tuple: (file_path_fragment, semantic_type, pattern_key)
3034
--- navegador/enrichment/rails.py
+++ navegador/enrichment/rails.py
@@ -19,11 +19,15 @@
19 def framework_name(self) -> str:
20 return "rails"
21
22 @property
23 def detection_patterns(self) -> list[str]:
24 return ["Gemfile", "config/routes.rb", "ApplicationController", "ActiveRecord"]
 
 
 
 
25
26 def enrich(self) -> EnrichmentResult:
27 result = EnrichmentResult()
28
29 # Each tuple: (file_path_fragment, semantic_type, pattern_key)
30
--- navegador/enrichment/rails.py
+++ navegador/enrichment/rails.py
@@ -19,11 +19,15 @@
19 def framework_name(self) -> str:
20 return "rails"
21
22 @property
23 def detection_patterns(self) -> list[str]:
24 return ["rails", "active_record", "action_controller"]
25
26 @property
27 def detection_files(self) -> list[str]:
28 return ["Gemfile"]
29
30 def enrich(self) -> EnrichmentResult:
31 result = EnrichmentResult()
32
33 # Each tuple: (file_path_fragment, semantic_type, pattern_key)
34
--- navegador/enrichment/react.py
+++ navegador/enrichment/react.py
@@ -19,11 +19,15 @@
1919
def framework_name(self) -> str:
2020
return "react"
2121
2222
@property
2323
def detection_patterns(self) -> list[str]:
24
- return ["react", "React", "next.config", "next/router"]
24
+ return ["react", "react-dom", "next"]
25
+
26
+ @property
27
+ def detection_files(self) -> list[str]:
28
+ return ["next.config.js", "next.config.ts", "next.config.mjs"]
2529
2630
def enrich(self) -> EnrichmentResult:
2731
result = EnrichmentResult()
2832
2933
# ── Components: functions/classes defined in .jsx or .tsx files ──────
3034
--- navegador/enrichment/react.py
+++ navegador/enrichment/react.py
@@ -19,11 +19,15 @@
19 def framework_name(self) -> str:
20 return "react"
21
22 @property
23 def detection_patterns(self) -> list[str]:
24 return ["react", "React", "next.config", "next/router"]
 
 
 
 
25
26 def enrich(self) -> EnrichmentResult:
27 result = EnrichmentResult()
28
29 # ── Components: functions/classes defined in .jsx or .tsx files ──────
30
--- navegador/enrichment/react.py
+++ navegador/enrichment/react.py
@@ -19,11 +19,15 @@
19 def framework_name(self) -> str:
20 return "react"
21
22 @property
23 def detection_patterns(self) -> list[str]:
24 return ["react", "react-dom", "next"]
25
26 @property
27 def detection_files(self) -> list[str]:
28 return ["next.config.js", "next.config.ts", "next.config.mjs"]
29
30 def enrich(self) -> EnrichmentResult:
31 result = EnrichmentResult()
32
33 # ── Components: functions/classes defined in .jsx or .tsx files ──────
34
--- navegador/enrichment/react_native.py
+++ navegador/enrichment/react_native.py
@@ -28,11 +28,15 @@
2828
def framework_name(self) -> str:
2929
return "react-native"
3030
3131
@property
3232
def detection_patterns(self) -> list[str]:
33
- return ["react-native", "React Native", "expo"]
33
+ return ["react-native", "expo"]
34
+
35
+ @property
36
+ def detection_files(self) -> list[str]:
37
+ return ["app.json"]
3438
3539
def enrich(self) -> EnrichmentResult:
3640
result = EnrichmentResult()
3741
3842
# ── Components: functions/classes in .jsx or .tsx files ──────────────
3943
--- navegador/enrichment/react_native.py
+++ navegador/enrichment/react_native.py
@@ -28,11 +28,15 @@
28 def framework_name(self) -> str:
29 return "react-native"
30
31 @property
32 def detection_patterns(self) -> list[str]:
33 return ["react-native", "React Native", "expo"]
 
 
 
 
34
35 def enrich(self) -> EnrichmentResult:
36 result = EnrichmentResult()
37
38 # ── Components: functions/classes in .jsx or .tsx files ──────────────
39
--- navegador/enrichment/react_native.py
+++ navegador/enrichment/react_native.py
@@ -28,11 +28,15 @@
28 def framework_name(self) -> str:
29 return "react-native"
30
31 @property
32 def detection_patterns(self) -> list[str]:
33 return ["react-native", "expo"]
34
35 @property
36 def detection_files(self) -> list[str]:
37 return ["app.json"]
38
39 def enrich(self) -> EnrichmentResult:
40 result = EnrichmentResult()
41
42 # ── Components: functions/classes in .jsx or .tsx files ──────────────
43
--- navegador/enrichment/spring.py
+++ navegador/enrichment/spring.py
@@ -18,11 +18,15 @@
1818
def framework_name(self) -> str:
1919
return "spring"
2020
2121
@property
2222
def detection_patterns(self) -> list[str]:
23
- return ["@SpringBootApplication", "spring-boot", "application.properties"]
23
+ return ["org.springframework"]
24
+
25
+ @property
26
+ def detection_files(self) -> list[str]:
27
+ return ["application.properties", "application.yml"]
2428
2529
def enrich(self) -> EnrichmentResult:
2630
result = EnrichmentResult()
2731
2832
# Each tuple: (annotation_fragment, semantic_type, pattern_key)
2933
--- navegador/enrichment/spring.py
+++ navegador/enrichment/spring.py
@@ -18,11 +18,15 @@
18 def framework_name(self) -> str:
19 return "spring"
20
21 @property
22 def detection_patterns(self) -> list[str]:
23 return ["@SpringBootApplication", "spring-boot", "application.properties"]
 
 
 
 
24
25 def enrich(self) -> EnrichmentResult:
26 result = EnrichmentResult()
27
28 # Each tuple: (annotation_fragment, semantic_type, pattern_key)
29
--- navegador/enrichment/spring.py
+++ navegador/enrichment/spring.py
@@ -18,11 +18,15 @@
18 def framework_name(self) -> str:
19 return "spring"
20
21 @property
22 def detection_patterns(self) -> list[str]:
23 return ["org.springframework"]
24
25 @property
26 def detection_files(self) -> list[str]:
27 return ["application.properties", "application.yml"]
28
29 def enrich(self) -> EnrichmentResult:
30 result = EnrichmentResult()
31
32 # Each tuple: (annotation_fragment, semantic_type, pattern_key)
33
+1 -1
--- pyproject.toml
+++ pyproject.toml
@@ -2,11 +2,11 @@
22
requires = ["setuptools>=69.0", "wheel"]
33
build-backend = "setuptools.build_meta"
44
55
[project]
66
name = "navegador"
7
-version = "0.7.3"
7
+version = "0.7.4"
88
description = "AST + knowledge graph context engine for AI coding agents"
99
readme = "README.md"
1010
license = "MIT"
1111
requires-python = ">=3.12"
1212
authors = [
1313
--- pyproject.toml
+++ pyproject.toml
@@ -2,11 +2,11 @@
2 requires = ["setuptools>=69.0", "wheel"]
3 build-backend = "setuptools.build_meta"
4
5 [project]
6 name = "navegador"
7 version = "0.7.3"
8 description = "AST + knowledge graph context engine for AI coding agents"
9 readme = "README.md"
10 license = "MIT"
11 requires-python = ">=3.12"
12 authors = [
13
--- pyproject.toml
+++ pyproject.toml
@@ -2,11 +2,11 @@
2 requires = ["setuptools>=69.0", "wheel"]
3 build-backend = "setuptools.build_meta"
4
5 [project]
6 name = "navegador"
7 version = "0.7.4"
8 description = "AST + knowledge graph context engine for AI coding agents"
9 readme = "README.md"
10 license = "MIT"
11 requires-python = ">=3.12"
12 authors = [
13
--- tests/test_enrichment_base.py
+++ tests/test_enrichment_base.py
@@ -127,16 +127,14 @@
127127
enricher.detect()
128128
129129
calls = store._graph.query.call_args_list
130130
# Two patterns → two queries
131131
assert len(calls) == 2
132
- _, kwargs0 = calls[0]
133
- _, kwargs1 = calls[1]
134132
params0 = calls[0][0][1] if len(calls[0][0]) > 1 else calls[0][1].get("params", {})
135133
params1 = calls[1][0][1] if len(calls[1][0]) > 1 else calls[1][1].get("params", {})
136
- assert params0 == {"pattern": "mock_module"}
137
- assert params1 == {"pattern": "mock_settings.py"}
134
+ assert params0 == {"name": "mock_module"}
135
+ assert params1 == {"name": "mock_settings.py"}
138136
139137
def test_stops_early_when_first_pattern_matches(self):
140138
store = _mock_store(result_set=[[5]])
141139
enricher = MockEnricher(store)
142140
assert enricher.detect() is True
143141
--- tests/test_enrichment_base.py
+++ tests/test_enrichment_base.py
@@ -127,16 +127,14 @@
127 enricher.detect()
128
129 calls = store._graph.query.call_args_list
130 # Two patterns → two queries
131 assert len(calls) == 2
132 _, kwargs0 = calls[0]
133 _, kwargs1 = calls[1]
134 params0 = calls[0][0][1] if len(calls[0][0]) > 1 else calls[0][1].get("params", {})
135 params1 = calls[1][0][1] if len(calls[1][0]) > 1 else calls[1][1].get("params", {})
136 assert params0 == {"pattern": "mock_module"}
137 assert params1 == {"pattern": "mock_settings.py"}
138
139 def test_stops_early_when_first_pattern_matches(self):
140 store = _mock_store(result_set=[[5]])
141 enricher = MockEnricher(store)
142 assert enricher.detect() is True
143
--- tests/test_enrichment_base.py
+++ tests/test_enrichment_base.py
@@ -127,16 +127,14 @@
127 enricher.detect()
128
129 calls = store._graph.query.call_args_list
130 # Two patterns → two queries
131 assert len(calls) == 2
 
 
132 params0 = calls[0][0][1] if len(calls[0][0]) > 1 else calls[0][1].get("params", {})
133 params1 = calls[1][0][1] if len(calls[1][0]) > 1 else calls[1][1].get("params", {})
134 assert params0 == {"name": "mock_module"}
135 assert params1 == {"name": "mock_settings.py"}
136
137 def test_stops_early_when_first_pattern_matches(self):
138 store = _mock_store(result_set=[[5]])
139 enricher = MockEnricher(store)
140 assert enricher.detect() is True
141
--- tests/test_enrichment_django.py
+++ tests/test_enrichment_django.py
@@ -36,25 +36,25 @@
3636
class TestDjangoEnricherMetadata:
3737
def test_framework_name(self):
3838
enricher = DjangoEnricher(_mock_store())
3939
assert enricher.framework_name == "django"
4040
41
- def test_detection_patterns_contains_manage_py(self):
41
+ def test_detection_patterns_contains_django(self):
4242
enricher = DjangoEnricher(_mock_store())
43
- assert "manage.py" in enricher.detection_patterns
43
+ assert "django" in enricher.detection_patterns
4444
4545
def test_detection_patterns_contains_django_conf(self):
4646
enricher = DjangoEnricher(_mock_store())
4747
assert "django.conf" in enricher.detection_patterns
4848
49
- def test_detection_patterns_contains_settings_py(self):
49
+ def test_detection_patterns_contains_django_db(self):
5050
enricher = DjangoEnricher(_mock_store())
51
- assert "settings.py" in enricher.detection_patterns
51
+ assert "django.db" in enricher.detection_patterns
5252
53
- def test_detection_patterns_contains_urls_py(self):
53
+ def test_detection_patterns_contains_django_http(self):
5454
enricher = DjangoEnricher(_mock_store())
55
- assert "urls.py" in enricher.detection_patterns
55
+ assert "django.http" in enricher.detection_patterns
5656
5757
def test_detection_patterns_is_list_of_strings(self):
5858
enricher = DjangoEnricher(_mock_store())
5959
patterns = enricher.detection_patterns
6060
assert isinstance(patterns, list)
@@ -103,11 +103,13 @@
103103
104104
def test_detect_tries_all_patterns_before_giving_up(self):
105105
store = _mock_store(result_set=[[0]])
106106
enricher = DjangoEnricher(store)
107107
enricher.detect()
108
- assert store._graph.query.call_count == len(enricher.detection_patterns)
108
+ # All detection_patterns are checked, then all detection_files
109
+ expected = len(enricher.detection_patterns) + len(enricher.detection_files)
110
+ assert store._graph.query.call_count == expected
109111
110112
111113
# ── enrich() — views ──────────────────────────────────────────────────────────
112114
113115
114116
--- tests/test_enrichment_django.py
+++ tests/test_enrichment_django.py
@@ -36,25 +36,25 @@
36 class TestDjangoEnricherMetadata:
37 def test_framework_name(self):
38 enricher = DjangoEnricher(_mock_store())
39 assert enricher.framework_name == "django"
40
41 def test_detection_patterns_contains_manage_py(self):
42 enricher = DjangoEnricher(_mock_store())
43 assert "manage.py" in enricher.detection_patterns
44
45 def test_detection_patterns_contains_django_conf(self):
46 enricher = DjangoEnricher(_mock_store())
47 assert "django.conf" in enricher.detection_patterns
48
49 def test_detection_patterns_contains_settings_py(self):
50 enricher = DjangoEnricher(_mock_store())
51 assert "settings.py" in enricher.detection_patterns
52
53 def test_detection_patterns_contains_urls_py(self):
54 enricher = DjangoEnricher(_mock_store())
55 assert "urls.py" in enricher.detection_patterns
56
57 def test_detection_patterns_is_list_of_strings(self):
58 enricher = DjangoEnricher(_mock_store())
59 patterns = enricher.detection_patterns
60 assert isinstance(patterns, list)
@@ -103,11 +103,13 @@
103
104 def test_detect_tries_all_patterns_before_giving_up(self):
105 store = _mock_store(result_set=[[0]])
106 enricher = DjangoEnricher(store)
107 enricher.detect()
108 assert store._graph.query.call_count == len(enricher.detection_patterns)
 
 
109
110
111 # ── enrich() — views ──────────────────────────────────────────────────────────
112
113
114
--- tests/test_enrichment_django.py
+++ tests/test_enrichment_django.py
@@ -36,25 +36,25 @@
36 class TestDjangoEnricherMetadata:
37 def test_framework_name(self):
38 enricher = DjangoEnricher(_mock_store())
39 assert enricher.framework_name == "django"
40
41 def test_detection_patterns_contains_django(self):
42 enricher = DjangoEnricher(_mock_store())
43 assert "django" in enricher.detection_patterns
44
45 def test_detection_patterns_contains_django_conf(self):
46 enricher = DjangoEnricher(_mock_store())
47 assert "django.conf" in enricher.detection_patterns
48
49 def test_detection_patterns_contains_django_db(self):
50 enricher = DjangoEnricher(_mock_store())
51 assert "django.db" in enricher.detection_patterns
52
53 def test_detection_patterns_contains_django_http(self):
54 enricher = DjangoEnricher(_mock_store())
55 assert "django.http" in enricher.detection_patterns
56
57 def test_detection_patterns_is_list_of_strings(self):
58 enricher = DjangoEnricher(_mock_store())
59 patterns = enricher.detection_patterns
60 assert isinstance(patterns, list)
@@ -103,11 +103,13 @@
103
104 def test_detect_tries_all_patterns_before_giving_up(self):
105 store = _mock_store(result_set=[[0]])
106 enricher = DjangoEnricher(store)
107 enricher.detect()
108 # All detection_patterns are checked, then all detection_files
109 expected = len(enricher.detection_patterns) + len(enricher.detection_files)
110 assert store._graph.query.call_count == expected
111
112
113 # ── enrich() — views ──────────────────────────────────────────────────────────
114
115
116
--- tests/test_enrichment_express.py
+++ tests/test_enrichment_express.py
@@ -57,22 +57,18 @@
5757
class TestDetectionPatterns:
5858
def test_contains_express_lowercase(self):
5959
enricher = ExpressEnricher(_mock_store())
6060
assert "express" in enricher.detection_patterns
6161
62
- def test_contains_Express_titlecase(self):
63
- enricher = ExpressEnricher(_mock_store())
64
- assert "Express" in enricher.detection_patterns
65
-
66
- def test_contains_app_listen(self):
67
- enricher = ExpressEnricher(_mock_store())
68
- assert "app.listen" in enricher.detection_patterns
69
-
7062
def test_returns_list(self):
7163
enricher = ExpressEnricher(_mock_store())
7264
assert isinstance(enricher.detection_patterns, list)
7365
66
+ def test_is_nonempty(self):
67
+ enricher = ExpressEnricher(_mock_store())
68
+ assert len(enricher.detection_patterns) >= 1
69
+
7470
7571
# ── detect() ─────────────────────────────────────────────────────────────────
7672
7773
7874
class TestDetect:
7975
--- tests/test_enrichment_express.py
+++ tests/test_enrichment_express.py
@@ -57,22 +57,18 @@
57 class TestDetectionPatterns:
58 def test_contains_express_lowercase(self):
59 enricher = ExpressEnricher(_mock_store())
60 assert "express" in enricher.detection_patterns
61
62 def test_contains_Express_titlecase(self):
63 enricher = ExpressEnricher(_mock_store())
64 assert "Express" in enricher.detection_patterns
65
66 def test_contains_app_listen(self):
67 enricher = ExpressEnricher(_mock_store())
68 assert "app.listen" in enricher.detection_patterns
69
70 def test_returns_list(self):
71 enricher = ExpressEnricher(_mock_store())
72 assert isinstance(enricher.detection_patterns, list)
73
 
 
 
 
74
75 # ── detect() ─────────────────────────────────────────────────────────────────
76
77
78 class TestDetect:
79
--- tests/test_enrichment_express.py
+++ tests/test_enrichment_express.py
@@ -57,22 +57,18 @@
57 class TestDetectionPatterns:
58 def test_contains_express_lowercase(self):
59 enricher = ExpressEnricher(_mock_store())
60 assert "express" in enricher.detection_patterns
61
 
 
 
 
 
 
 
 
62 def test_returns_list(self):
63 enricher = ExpressEnricher(_mock_store())
64 assert isinstance(enricher.detection_patterns, list)
65
66 def test_is_nonempty(self):
67 enricher = ExpressEnricher(_mock_store())
68 assert len(enricher.detection_patterns) >= 1
69
70
71 # ── detect() ─────────────────────────────────────────────────────────────────
72
73
74 class TestDetect:
75
--- tests/test_enrichment_fastapi.py
+++ tests/test_enrichment_fastapi.py
@@ -56,17 +56,19 @@
5656
5757
def test_detection_patterns_includes_fastapi_lowercase(self):
5858
store = _mock_store()
5959
assert "fastapi" in FastAPIEnricher(store).detection_patterns
6060
61
- def test_detection_patterns_includes_fastapi_class(self):
61
+ def test_detection_patterns_is_list_of_strings(self):
6262
store = _mock_store()
63
- assert "FastAPI" in FastAPIEnricher(store).detection_patterns
63
+ patterns = FastAPIEnricher(store).detection_patterns
64
+ assert isinstance(patterns, list)
65
+ assert all(isinstance(p, str) for p in patterns)
6466
65
- def test_detection_patterns_includes_apirouter(self):
67
+ def test_detection_patterns_is_nonempty(self):
6668
store = _mock_store()
67
- assert "APIRouter" in FastAPIEnricher(store).detection_patterns
69
+ assert len(FastAPIEnricher(store).detection_patterns) >= 1
6870
6971
def test_is_subclass_of_framework_enricher(self):
7072
store = _mock_store()
7173
assert isinstance(FastAPIEnricher(store), FrameworkEnricher)
7274
@@ -85,15 +87,17 @@
8587
8688
def test_detect_returns_false_on_empty_result_set(self):
8789
store = _mock_store(result_set=[])
8890
assert FastAPIEnricher(store).detect() is False
8991
90
- def test_detect_queries_all_three_patterns_when_no_match(self):
92
+ def test_detect_queries_all_patterns_when_no_match(self):
9193
store = _mock_store(result_set=[[0]])
92
- FastAPIEnricher(store).detect()
93
- # Three detection patterns → three queries
94
- assert store._graph.query.call_count == 3
94
+ enricher = FastAPIEnricher(store)
95
+ enricher.detect()
96
+ # All detection_patterns queried when no match is found
97
+ expected = len(enricher.detection_patterns) + len(enricher.detection_files)
98
+ assert store._graph.query.call_count == expected
9599
96100
def test_detect_short_circuits_on_first_match(self):
97101
store = _mock_store(result_set=[[7]])
98102
FastAPIEnricher(store).detect()
99103
assert store._graph.query.call_count == 1
100104
--- tests/test_enrichment_fastapi.py
+++ tests/test_enrichment_fastapi.py
@@ -56,17 +56,19 @@
56
57 def test_detection_patterns_includes_fastapi_lowercase(self):
58 store = _mock_store()
59 assert "fastapi" in FastAPIEnricher(store).detection_patterns
60
61 def test_detection_patterns_includes_fastapi_class(self):
62 store = _mock_store()
63 assert "FastAPI" in FastAPIEnricher(store).detection_patterns
 
 
64
65 def test_detection_patterns_includes_apirouter(self):
66 store = _mock_store()
67 assert "APIRouter" in FastAPIEnricher(store).detection_patterns
68
69 def test_is_subclass_of_framework_enricher(self):
70 store = _mock_store()
71 assert isinstance(FastAPIEnricher(store), FrameworkEnricher)
72
@@ -85,15 +87,17 @@
85
86 def test_detect_returns_false_on_empty_result_set(self):
87 store = _mock_store(result_set=[])
88 assert FastAPIEnricher(store).detect() is False
89
90 def test_detect_queries_all_three_patterns_when_no_match(self):
91 store = _mock_store(result_set=[[0]])
92 FastAPIEnricher(store).detect()
93 # Three detection patterns → three queries
94 assert store._graph.query.call_count == 3
 
 
95
96 def test_detect_short_circuits_on_first_match(self):
97 store = _mock_store(result_set=[[7]])
98 FastAPIEnricher(store).detect()
99 assert store._graph.query.call_count == 1
100
--- tests/test_enrichment_fastapi.py
+++ tests/test_enrichment_fastapi.py
@@ -56,17 +56,19 @@
56
57 def test_detection_patterns_includes_fastapi_lowercase(self):
58 store = _mock_store()
59 assert "fastapi" in FastAPIEnricher(store).detection_patterns
60
61 def test_detection_patterns_is_list_of_strings(self):
62 store = _mock_store()
63 patterns = FastAPIEnricher(store).detection_patterns
64 assert isinstance(patterns, list)
65 assert all(isinstance(p, str) for p in patterns)
66
67 def test_detection_patterns_is_nonempty(self):
68 store = _mock_store()
69 assert len(FastAPIEnricher(store).detection_patterns) >= 1
70
71 def test_is_subclass_of_framework_enricher(self):
72 store = _mock_store()
73 assert isinstance(FastAPIEnricher(store), FrameworkEnricher)
74
@@ -85,15 +87,17 @@
87
88 def test_detect_returns_false_on_empty_result_set(self):
89 store = _mock_store(result_set=[])
90 assert FastAPIEnricher(store).detect() is False
91
92 def test_detect_queries_all_patterns_when_no_match(self):
93 store = _mock_store(result_set=[[0]])
94 enricher = FastAPIEnricher(store)
95 enricher.detect()
96 # All detection_patterns queried when no match is found
97 expected = len(enricher.detection_patterns) + len(enricher.detection_files)
98 assert store._graph.query.call_count == expected
99
100 def test_detect_short_circuits_on_first_match(self):
101 store = _mock_store(result_set=[[7]])
102 FastAPIEnricher(store).detect()
103 assert store._graph.query.call_count == 1
104
--- tests/test_enrichment_laravel.py
+++ tests/test_enrichment_laravel.py
@@ -39,25 +39,25 @@
3939
assert LaravelEnricher(store).framework_name == "laravel"
4040
4141
def test_is_framework_enricher_subclass(self):
4242
assert issubclass(LaravelEnricher, FrameworkEnricher)
4343
44
- def test_detection_patterns_contains_artisan(self):
45
- store = _mock_store()
46
- assert "artisan" in LaravelEnricher(store).detection_patterns
47
-
4844
def test_detection_patterns_contains_illuminate(self):
4945
store = _mock_store()
5046
assert "Illuminate" in LaravelEnricher(store).detection_patterns
5147
52
- def test_detection_patterns_contains_http_controllers(self):
48
+ def test_detection_files_contains_artisan(self):
49
+ store = _mock_store()
50
+ assert "artisan" in LaravelEnricher(store).detection_files
51
+
52
+ def test_detection_patterns_has_one_entry(self):
5353
store = _mock_store()
54
- assert "app/Http/Controllers" in LaravelEnricher(store).detection_patterns
54
+ assert len(LaravelEnricher(store).detection_patterns) == 1
5555
56
- def test_detection_patterns_has_three_entries(self):
56
+ def test_detection_files_is_nonempty(self):
5757
store = _mock_store()
58
- assert len(LaravelEnricher(store).detection_patterns) == 3
58
+ assert len(LaravelEnricher(store).detection_files) >= 1
5959
6060
6161
# ── enrich() return type ──────────────────────────────────────────────────────
6262
6363
6464
--- tests/test_enrichment_laravel.py
+++ tests/test_enrichment_laravel.py
@@ -39,25 +39,25 @@
39 assert LaravelEnricher(store).framework_name == "laravel"
40
41 def test_is_framework_enricher_subclass(self):
42 assert issubclass(LaravelEnricher, FrameworkEnricher)
43
44 def test_detection_patterns_contains_artisan(self):
45 store = _mock_store()
46 assert "artisan" in LaravelEnricher(store).detection_patterns
47
48 def test_detection_patterns_contains_illuminate(self):
49 store = _mock_store()
50 assert "Illuminate" in LaravelEnricher(store).detection_patterns
51
52 def test_detection_patterns_contains_http_controllers(self):
 
 
 
 
53 store = _mock_store()
54 assert "app/Http/Controllers" in LaravelEnricher(store).detection_patterns
55
56 def test_detection_patterns_has_three_entries(self):
57 store = _mock_store()
58 assert len(LaravelEnricher(store).detection_patterns) == 3
59
60
61 # ── enrich() return type ──────────────────────────────────────────────────────
62
63
64
--- tests/test_enrichment_laravel.py
+++ tests/test_enrichment_laravel.py
@@ -39,25 +39,25 @@
39 assert LaravelEnricher(store).framework_name == "laravel"
40
41 def test_is_framework_enricher_subclass(self):
42 assert issubclass(LaravelEnricher, FrameworkEnricher)
43
 
 
 
 
44 def test_detection_patterns_contains_illuminate(self):
45 store = _mock_store()
46 assert "Illuminate" in LaravelEnricher(store).detection_patterns
47
48 def test_detection_files_contains_artisan(self):
49 store = _mock_store()
50 assert "artisan" in LaravelEnricher(store).detection_files
51
52 def test_detection_patterns_has_one_entry(self):
53 store = _mock_store()
54 assert len(LaravelEnricher(store).detection_patterns) == 1
55
56 def test_detection_files_is_nonempty(self):
57 store = _mock_store()
58 assert len(LaravelEnricher(store).detection_files) >= 1
59
60
61 # ── enrich() return type ──────────────────────────────────────────────────────
62
63
64
--- tests/test_enrichment_rails.py
+++ tests/test_enrichment_rails.py
@@ -39,29 +39,29 @@
3939
assert RailsEnricher(store).framework_name == "rails"
4040
4141
def test_is_framework_enricher_subclass(self):
4242
assert issubclass(RailsEnricher, FrameworkEnricher)
4343
44
- def test_detection_patterns_contains_gemfile(self):
45
- store = _mock_store()
46
- assert "Gemfile" in RailsEnricher(store).detection_patterns
47
-
48
- def test_detection_patterns_contains_routes(self):
49
- store = _mock_store()
50
- assert "config/routes.rb" in RailsEnricher(store).detection_patterns
51
-
52
- def test_detection_patterns_contains_application_controller(self):
53
- store = _mock_store()
54
- assert "ApplicationController" in RailsEnricher(store).detection_patterns
44
+ def test_detection_patterns_contains_rails(self):
45
+ store = _mock_store()
46
+ assert "rails" in RailsEnricher(store).detection_patterns
5547
5648
def test_detection_patterns_contains_active_record(self):
5749
store = _mock_store()
58
- assert "ActiveRecord" in RailsEnricher(store).detection_patterns
50
+ assert "active_record" in RailsEnricher(store).detection_patterns
51
+
52
+ def test_detection_patterns_contains_action_controller(self):
53
+ store = _mock_store()
54
+ assert "action_controller" in RailsEnricher(store).detection_patterns
55
+
56
+ def test_detection_files_contains_gemfile(self):
57
+ store = _mock_store()
58
+ assert "Gemfile" in RailsEnricher(store).detection_files
5959
60
- def test_detection_patterns_has_four_entries(self):
60
+ def test_detection_patterns_has_three_entries(self):
6161
store = _mock_store()
62
- assert len(RailsEnricher(store).detection_patterns) == 4
62
+ assert len(RailsEnricher(store).detection_patterns) == 3
6363
6464
6565
# ── enrich() return type ──────────────────────────────────────────────────────
6666
6767
6868
--- tests/test_enrichment_rails.py
+++ tests/test_enrichment_rails.py
@@ -39,29 +39,29 @@
39 assert RailsEnricher(store).framework_name == "rails"
40
41 def test_is_framework_enricher_subclass(self):
42 assert issubclass(RailsEnricher, FrameworkEnricher)
43
44 def test_detection_patterns_contains_gemfile(self):
45 store = _mock_store()
46 assert "Gemfile" in RailsEnricher(store).detection_patterns
47
48 def test_detection_patterns_contains_routes(self):
49 store = _mock_store()
50 assert "config/routes.rb" in RailsEnricher(store).detection_patterns
51
52 def test_detection_patterns_contains_application_controller(self):
53 store = _mock_store()
54 assert "ApplicationController" in RailsEnricher(store).detection_patterns
55
56 def test_detection_patterns_contains_active_record(self):
57 store = _mock_store()
58 assert "ActiveRecord" in RailsEnricher(store).detection_patterns
 
 
 
 
 
 
 
 
59
60 def test_detection_patterns_has_four_entries(self):
61 store = _mock_store()
62 assert len(RailsEnricher(store).detection_patterns) == 4
63
64
65 # ── enrich() return type ──────────────────────────────────────────────────────
66
67
68
--- tests/test_enrichment_rails.py
+++ tests/test_enrichment_rails.py
@@ -39,29 +39,29 @@
39 assert RailsEnricher(store).framework_name == "rails"
40
41 def test_is_framework_enricher_subclass(self):
42 assert issubclass(RailsEnricher, FrameworkEnricher)
43
44 def test_detection_patterns_contains_rails(self):
45 store = _mock_store()
46 assert "rails" in RailsEnricher(store).detection_patterns
 
 
 
 
 
 
 
 
47
48 def test_detection_patterns_contains_active_record(self):
49 store = _mock_store()
50 assert "active_record" in RailsEnricher(store).detection_patterns
51
52 def test_detection_patterns_contains_action_controller(self):
53 store = _mock_store()
54 assert "action_controller" in RailsEnricher(store).detection_patterns
55
56 def test_detection_files_contains_gemfile(self):
57 store = _mock_store()
58 assert "Gemfile" in RailsEnricher(store).detection_files
59
60 def test_detection_patterns_has_three_entries(self):
61 store = _mock_store()
62 assert len(RailsEnricher(store).detection_patterns) == 3
63
64
65 # ── enrich() return type ──────────────────────────────────────────────────────
66
67
68
--- tests/test_enrichment_react.py
+++ tests/test_enrichment_react.py
@@ -59,25 +59,26 @@
5959
class TestDetectionPatterns:
6060
def test_contains_react(self):
6161
enricher = ReactEnricher(_mock_store())
6262
assert "react" in enricher.detection_patterns
6363
64
- def test_contains_React(self):
65
- enricher = ReactEnricher(_mock_store())
66
- assert "React" in enricher.detection_patterns
67
-
68
- def test_contains_next_config(self):
69
- enricher = ReactEnricher(_mock_store())
70
- assert "next.config" in enricher.detection_patterns
71
-
72
- def test_contains_next_router(self):
73
- enricher = ReactEnricher(_mock_store())
74
- assert "next/router" in enricher.detection_patterns
64
+ def test_contains_react_dom(self):
65
+ enricher = ReactEnricher(_mock_store())
66
+ assert "react-dom" in enricher.detection_patterns
67
+
68
+ def test_contains_next(self):
69
+ enricher = ReactEnricher(_mock_store())
70
+ assert "next" in enricher.detection_patterns
7571
7672
def test_returns_list(self):
7773
enricher = ReactEnricher(_mock_store())
7874
assert isinstance(enricher.detection_patterns, list)
75
+
76
+ def test_detection_files_contains_next_config_variants(self):
77
+ enricher = ReactEnricher(_mock_store())
78
+ files = enricher.detection_files
79
+ assert any("next.config" in f for f in files)
7980
8081
8182
# ── detect() ─────────────────────────────────────────────────────────────────
8283
8384
8485
--- tests/test_enrichment_react.py
+++ tests/test_enrichment_react.py
@@ -59,25 +59,26 @@
59 class TestDetectionPatterns:
60 def test_contains_react(self):
61 enricher = ReactEnricher(_mock_store())
62 assert "react" in enricher.detection_patterns
63
64 def test_contains_React(self):
65 enricher = ReactEnricher(_mock_store())
66 assert "React" in enricher.detection_patterns
67
68 def test_contains_next_config(self):
69 enricher = ReactEnricher(_mock_store())
70 assert "next.config" in enricher.detection_patterns
71
72 def test_contains_next_router(self):
73 enricher = ReactEnricher(_mock_store())
74 assert "next/router" in enricher.detection_patterns
75
76 def test_returns_list(self):
77 enricher = ReactEnricher(_mock_store())
78 assert isinstance(enricher.detection_patterns, list)
 
 
 
 
 
79
80
81 # ── detect() ─────────────────────────────────────────────────────────────────
82
83
84
--- tests/test_enrichment_react.py
+++ tests/test_enrichment_react.py
@@ -59,25 +59,26 @@
59 class TestDetectionPatterns:
60 def test_contains_react(self):
61 enricher = ReactEnricher(_mock_store())
62 assert "react" in enricher.detection_patterns
63
64 def test_contains_react_dom(self):
65 enricher = ReactEnricher(_mock_store())
66 assert "react-dom" in enricher.detection_patterns
67
68 def test_contains_next(self):
69 enricher = ReactEnricher(_mock_store())
70 assert "next" in enricher.detection_patterns
 
 
 
 
71
72 def test_returns_list(self):
73 enricher = ReactEnricher(_mock_store())
74 assert isinstance(enricher.detection_patterns, list)
75
76 def test_detection_files_contains_next_config_variants(self):
77 enricher = ReactEnricher(_mock_store())
78 files = enricher.detection_files
79 assert any("next.config" in f for f in files)
80
81
82 # ── detect() ─────────────────────────────────────────────────────────────────
83
84
85
--- tests/test_enrichment_react_native.py
+++ tests/test_enrichment_react_native.py
@@ -57,22 +57,22 @@
5757
class TestDetectionPatterns:
5858
def test_contains_react_native_hyphenated(self):
5959
enricher = ReactNativeEnricher(_mock_store())
6060
assert "react-native" in enricher.detection_patterns
6161
62
- def test_contains_React_Native_titlecase(self):
63
- enricher = ReactNativeEnricher(_mock_store())
64
- assert "React Native" in enricher.detection_patterns
65
-
6662
def test_contains_expo(self):
6763
enricher = ReactNativeEnricher(_mock_store())
6864
assert "expo" in enricher.detection_patterns
6965
7066
def test_returns_list(self):
7167
enricher = ReactNativeEnricher(_mock_store())
7268
assert isinstance(enricher.detection_patterns, list)
7369
70
+ def test_has_at_least_two_patterns(self):
71
+ enricher = ReactNativeEnricher(_mock_store())
72
+ assert len(enricher.detection_patterns) >= 2
73
+
7474
7575
# ── detect() ─────────────────────────────────────────────────────────────────
7676
7777
7878
class TestDetect:
7979
--- tests/test_enrichment_react_native.py
+++ tests/test_enrichment_react_native.py
@@ -57,22 +57,22 @@
57 class TestDetectionPatterns:
58 def test_contains_react_native_hyphenated(self):
59 enricher = ReactNativeEnricher(_mock_store())
60 assert "react-native" in enricher.detection_patterns
61
62 def test_contains_React_Native_titlecase(self):
63 enricher = ReactNativeEnricher(_mock_store())
64 assert "React Native" in enricher.detection_patterns
65
66 def test_contains_expo(self):
67 enricher = ReactNativeEnricher(_mock_store())
68 assert "expo" in enricher.detection_patterns
69
70 def test_returns_list(self):
71 enricher = ReactNativeEnricher(_mock_store())
72 assert isinstance(enricher.detection_patterns, list)
73
 
 
 
 
74
75 # ── detect() ─────────────────────────────────────────────────────────────────
76
77
78 class TestDetect:
79
--- tests/test_enrichment_react_native.py
+++ tests/test_enrichment_react_native.py
@@ -57,22 +57,22 @@
57 class TestDetectionPatterns:
58 def test_contains_react_native_hyphenated(self):
59 enricher = ReactNativeEnricher(_mock_store())
60 assert "react-native" in enricher.detection_patterns
61
 
 
 
 
62 def test_contains_expo(self):
63 enricher = ReactNativeEnricher(_mock_store())
64 assert "expo" in enricher.detection_patterns
65
66 def test_returns_list(self):
67 enricher = ReactNativeEnricher(_mock_store())
68 assert isinstance(enricher.detection_patterns, list)
69
70 def test_has_at_least_two_patterns(self):
71 enricher = ReactNativeEnricher(_mock_store())
72 assert len(enricher.detection_patterns) >= 2
73
74
75 # ── detect() ─────────────────────────────────────────────────────────────────
76
77
78 class TestDetect:
79
--- tests/test_enrichment_spring.py
+++ tests/test_enrichment_spring.py
@@ -39,25 +39,25 @@
3939
assert SpringEnricher(store).framework_name == "spring"
4040
4141
def test_is_framework_enricher_subclass(self):
4242
assert issubclass(SpringEnricher, FrameworkEnricher)
4343
44
- def test_detection_patterns_contains_spring_boot_application(self):
45
- store = _mock_store()
46
- assert "@SpringBootApplication" in SpringEnricher(store).detection_patterns
47
-
48
- def test_detection_patterns_contains_spring_boot(self):
49
- store = _mock_store()
50
- assert "spring-boot" in SpringEnricher(store).detection_patterns
51
-
52
- def test_detection_patterns_contains_application_properties(self):
53
- store = _mock_store()
54
- assert "application.properties" in SpringEnricher(store).detection_patterns
55
-
56
- def test_detection_patterns_has_three_entries(self):
57
- store = _mock_store()
58
- assert len(SpringEnricher(store).detection_patterns) == 3
44
+ def test_detection_patterns_contains_org_springframework(self):
45
+ store = _mock_store()
46
+ assert "org.springframework" in SpringEnricher(store).detection_patterns
47
+
48
+ def test_detection_files_contains_application_properties(self):
49
+ store = _mock_store()
50
+ assert "application.properties" in SpringEnricher(store).detection_files
51
+
52
+ def test_detection_files_contains_application_yml(self):
53
+ store = _mock_store()
54
+ assert "application.yml" in SpringEnricher(store).detection_files
55
+
56
+ def test_detection_patterns_has_one_entry(self):
57
+ store = _mock_store()
58
+ assert len(SpringEnricher(store).detection_patterns) == 1
5959
6060
6161
# ── enrich() return type ──────────────────────────────────────────────────────
6262
6363
6464
--- tests/test_enrichment_spring.py
+++ tests/test_enrichment_spring.py
@@ -39,25 +39,25 @@
39 assert SpringEnricher(store).framework_name == "spring"
40
41 def test_is_framework_enricher_subclass(self):
42 assert issubclass(SpringEnricher, FrameworkEnricher)
43
44 def test_detection_patterns_contains_spring_boot_application(self):
45 store = _mock_store()
46 assert "@SpringBootApplication" in SpringEnricher(store).detection_patterns
47
48 def test_detection_patterns_contains_spring_boot(self):
49 store = _mock_store()
50 assert "spring-boot" in SpringEnricher(store).detection_patterns
51
52 def test_detection_patterns_contains_application_properties(self):
53 store = _mock_store()
54 assert "application.properties" in SpringEnricher(store).detection_patterns
55
56 def test_detection_patterns_has_three_entries(self):
57 store = _mock_store()
58 assert len(SpringEnricher(store).detection_patterns) == 3
59
60
61 # ── enrich() return type ──────────────────────────────────────────────────────
62
63
64
--- tests/test_enrichment_spring.py
+++ tests/test_enrichment_spring.py
@@ -39,25 +39,25 @@
39 assert SpringEnricher(store).framework_name == "spring"
40
41 def test_is_framework_enricher_subclass(self):
42 assert issubclass(SpringEnricher, FrameworkEnricher)
43
44 def test_detection_patterns_contains_org_springframework(self):
45 store = _mock_store()
46 assert "org.springframework" in SpringEnricher(store).detection_patterns
47
48 def test_detection_files_contains_application_properties(self):
49 store = _mock_store()
50 assert "application.properties" in SpringEnricher(store).detection_files
51
52 def test_detection_files_contains_application_yml(self):
53 store = _mock_store()
54 assert "application.yml" in SpringEnricher(store).detection_files
55
56 def test_detection_patterns_has_one_entry(self):
57 store = _mock_store()
58 assert len(SpringEnricher(store).detection_patterns) == 1
59
60
61 # ── enrich() return type ──────────────────────────────────────────────────────
62
63
64

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button