nextog-cli 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. nextog/__init__.py +4 -0
  2. nextog/cli.py +545 -0
  3. nextog/config/__init__.py +1 -0
  4. nextog/config/settings.py +132 -0
  5. nextog/core/__init__.py +1 -0
  6. nextog/core/engine.py +193 -0
  7. nextog/core/permissions.py +129 -0
  8. nextog/core/privacy.py +130 -0
  9. nextog/core/reporter.py +204 -0
  10. nextog/core/runner.py +236 -0
  11. nextog/data/__init__.py +1 -0
  12. nextog/data/local_db.py +367 -0
  13. nextog/data/models.py +72 -0
  14. nextog/data/sync.py +65 -0
  15. nextog/engines/__init__.py +1 -0
  16. nextog/engines/api/__init__.py +1 -0
  17. nextog/engines/api/graphql.py +54 -0
  18. nextog/engines/api/rest.py +346 -0
  19. nextog/engines/api/websocket.py +59 -0
  20. nextog/engines/embedded/__init__.py +1 -0
  21. nextog/engines/embedded/firmware.py +53 -0
  22. nextog/engines/embedded/hardware.py +330 -0
  23. nextog/engines/mobile/__init__.py +1 -0
  24. nextog/engines/mobile/android.py +333 -0
  25. nextog/engines/mobile/cross.py +48 -0
  26. nextog/engines/mobile/ios.py +46 -0
  27. nextog/engines/system/__init__.py +1 -0
  28. nextog/engines/system/load.py +121 -0
  29. nextog/engines/system/performance.py +128 -0
  30. nextog/engines/system/security.py +170 -0
  31. nextog/engines/web/__init__.py +1 -0
  32. nextog/engines/web/accessibility.py +191 -0
  33. nextog/engines/web/browser.py +387 -0
  34. nextog/engines/web/elements.py +285 -0
  35. nextog/engines/web/responsive.py +79 -0
  36. nextog/live/__init__.py +1 -0
  37. nextog/live/dashboard.py +30 -0
  38. nextog/live/panel.py +325 -0
  39. nextog/reports/__init__.py +1359 -0
  40. nextog/training/__init__.py +1 -0
  41. nextog/training/learner.py +269 -0
  42. nextog/training/patterns.py +102 -0
  43. nextog/utils/__init__.py +1 -0
  44. nextog/utils/helpers.py +91 -0
  45. nextog/utils/logger.py +37 -0
  46. nextog/utils/validators.py +98 -0
  47. nextog_cli-1.0.0.dist-info/METADATA +344 -0
  48. nextog_cli-1.0.0.dist-info/RECORD +51 -0
  49. nextog_cli-1.0.0.dist-info/WHEEL +5 -0
  50. nextog_cli-1.0.0.dist-info/entry_points.txt +2 -0
  51. nextog_cli-1.0.0.dist-info/top_level.txt +1 -0
nextog/core/runner.py ADDED
@@ -0,0 +1,236 @@
1
+ """
2
+ Test Runner - Orchestrates test execution across all engines
3
+ """
4
+
5
+ import asyncio
6
+ from typing import Dict, List, Optional, Any
7
+ from pathlib import Path
8
+ from datetime import datetime
9
+ from rich.console import Console
10
+ from rich.progress import Progress, SpinnerColumn, BarColumn, TextColumn
11
+
12
+ console = Console()
13
+
14
+
15
+ class TestRunner:
16
+ """Orchestrates running tests across multiple engines"""
17
+
18
+ def __init__(self, settings, db, privacy, permissions):
19
+ self.settings = settings
20
+ self.db = db
21
+ self.privacy = privacy
22
+ self.permissions = permissions
23
+
24
+ def run_all(self, config_path: str, coverage_target: int = 90, parallel: bool = True) -> Dict:
25
+ """Run all tests from configuration"""
26
+ config = self._load_config(config_path)
27
+ results = {
28
+ "categories": {},
29
+ "total_coverage": 0,
30
+ "timestamp": datetime.now().isoformat(),
31
+ "duration": 0,
32
+ }
33
+
34
+ engines_config = config.get("engines", {})
35
+ total_phases = len(engines_config)
36
+
37
+ with Progress(
38
+ SpinnerColumn(),
39
+ TextColumn("[progress.description]{task.description}"),
40
+ BarColumn(),
41
+ TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
42
+ console=console,
43
+ ) as progress:
44
+ overall = progress.add_task("Running all tests...", total=total_phases)
45
+
46
+ for engine_name, engine_config in engines_config.items():
47
+ progress.update(overall, description=f"Running {engine_name} tests...")
48
+
49
+ engine = self._get_engine(engine_name)
50
+ if engine:
51
+ engine_results = engine.run_tests(**engine_config)
52
+ results["categories"][engine_name] = engine_results
53
+ results["total_coverage"] = self._calc_coverage(results)
54
+
55
+ progress.advance(overall)
56
+
57
+ results["duration"] = self._calculate_duration(results)
58
+ self.db.save_test_results(results)
59
+ self.privacy.record_activity("full_test_run", results)
60
+
61
+ return results
62
+
63
+ def _load_config(self, config_path: str) -> Dict:
64
+ """Load test configuration from YAML"""
65
+ import yaml
66
+ path = Path(config_path)
67
+ if path.exists():
68
+ with open(path) as f:
69
+ return yaml.safe_load(f) or {}
70
+ return {}
71
+
72
+ def _get_engine(self, name: str):
73
+ """Get engine instance by name"""
74
+ engine_map = {
75
+ "web": ("nextog.engines.web.browser", "WebTestEngine"),
76
+ "mobile": ("nextog.engines.mobile.android", "MobileTestEngine"),
77
+ "api": ("nextog.engines.api.rest", "APITestEngine"),
78
+ "embedded": ("nextog.engines.embedded.hardware", "EmbeddedTestEngine"),
79
+ }
80
+
81
+ if name not in engine_map:
82
+ return None
83
+
84
+ module_path, class_name = engine_map[name]
85
+ import importlib
86
+ module = importlib.import_module(module_path)
87
+ engine_class = getattr(module, class_name)
88
+ return engine_class(self.settings, self.db, self.privacy)
89
+
90
+ def _calc_coverage(self, results: Dict) -> float:
91
+ """Calculate total coverage from all results"""
92
+ total = 0
93
+ count = 0
94
+ for cat_data in results.get("categories", {}).values():
95
+ if isinstance(cat_data, dict) and "coverage" in cat_data:
96
+ total += cat_data["coverage"]
97
+ count += 1
98
+ return round(total / count, 2) if count > 0 else 0
99
+
100
+ def _calculate_duration(self, results: Dict) -> float:
101
+ """Calculate total test duration"""
102
+ return sum(
103
+ cat.get("duration", 0)
104
+ for cat in results.get("categories", {}).values()
105
+ if isinstance(cat, dict)
106
+ )
107
+
108
+
109
+ class ProjectInitializer:
110
+ """Initialize new nextOG projects"""
111
+
112
+ def __init__(self, settings, db):
113
+ self.settings = settings
114
+ self.db = db
115
+
116
+ def create_project(self, name: str, template: str = "full"):
117
+ """Create a new project structure"""
118
+ project_dir = Path(name)
119
+ project_dir.mkdir(parents=True, exist_ok=True)
120
+
121
+ # Create .nextog directory
122
+ nextog_dir = project_dir / ".nextog"
123
+ nextog_dir.mkdir(exist_ok=True)
124
+ (nextog_dir / "tests").mkdir(exist_ok=True)
125
+ (nextog_dir / "reports").mkdir(exist_ok=True)
126
+ (nextog_dir / "data").mkdir(exist_ok=True)
127
+
128
+ # Generate config based on template
129
+ config = self._get_template(template, name)
130
+
131
+ import yaml
132
+ with open(nextog_dir / "config.yaml", "w") as f:
133
+ yaml.dump(config, f, default_flow_style=False)
134
+
135
+ # Create initial test files based on template
136
+ self._create_test_files(nextog_dir / "tests", template)
137
+
138
+ def _get_template(self, template: str, name: str) -> Dict:
139
+ """Get configuration template"""
140
+ templates = {
141
+ "full": {
142
+ "project": name,
143
+ "version": "1.0",
144
+ "engines": {
145
+ "web": {"url": "http://localhost:3000", "browser": "chromium", "responsive": True, "accessibility": True},
146
+ "api": {"base_url": "http://localhost:3000/api", "spec": "openapi.yaml"},
147
+ "mobile": {"platform": "android", "app": "./app.apk"},
148
+ "embedded": {"target": "localhost", "protocol": "mqtt"},
149
+ },
150
+ "coverage_target": 90,
151
+ "parallel": True,
152
+ },
153
+ "web": {
154
+ "project": name,
155
+ "version": "1.0",
156
+ "engines": {
157
+ "web": {"url": "http://localhost:3000", "browser": "chromium", "responsive": True, "accessibility": True},
158
+ },
159
+ "coverage_target": 90,
160
+ },
161
+ "mobile": {
162
+ "project": name,
163
+ "version": "1.0",
164
+ "engines": {
165
+ "mobile": {"platform": "android", "app": "./app.apk"},
166
+ },
167
+ "coverage_target": 90,
168
+ },
169
+ "api": {
170
+ "project": name,
171
+ "version": "1.0",
172
+ "engines": {
173
+ "api": {"base_url": "http://localhost:3000/api", "spec": "openapi.yaml"},
174
+ },
175
+ "coverage_target": 90,
176
+ },
177
+ "minimal": {
178
+ "project": name,
179
+ "version": "1.0",
180
+ "engines": {},
181
+ "coverage_target": 60,
182
+ },
183
+ }
184
+ return templates.get(template, templates["full"])
185
+
186
+ def _create_test_files(self, test_dir: Path, template: str):
187
+ """Create initial test files"""
188
+ import yaml
189
+
190
+ # Create sample web test
191
+ if template in ("full", "web"):
192
+ web_test = {
193
+ "name": "Sample Web Tests",
194
+ "url": "http://localhost:3000",
195
+ "tests": [
196
+ {
197
+ "name": "Homepage loads",
198
+ "type": "smoke",
199
+ "steps": [{"action": "navigate", "target": "/"}],
200
+ "assertions": [{"type": "status", "expected": 200}],
201
+ },
202
+ {
203
+ "name": "Login form works",
204
+ "type": "functional",
205
+ "steps": [
206
+ {"action": "navigate", "target": "/login"},
207
+ {"action": "fill", "target": "#email", "value": "test@test.com"},
208
+ {"action": "fill", "target": "#password", "value": "password"},
209
+ {"action": "click", "target": "#submit"},
210
+ ],
211
+ "assertions": [{"type": "url_contains", "expected": "/dashboard"}],
212
+ },
213
+ ],
214
+ }
215
+ with open(test_dir / "web_tests.yaml", "w") as f:
216
+ yaml.dump(web_test, f, default_flow_style=False)
217
+
218
+ # Create sample API test
219
+ if template in ("full", "api"):
220
+ api_test = {
221
+ "name": "Sample API Tests",
222
+ "base_url": "http://localhost:3000/api",
223
+ "tests": [
224
+ {
225
+ "name": "Health check",
226
+ "method": "GET",
227
+ "endpoint": "/health",
228
+ "assertions": [
229
+ {"type": "status_code", "expected": 200},
230
+ {"type": "response_time", "max_ms": 1000},
231
+ ],
232
+ },
233
+ ],
234
+ }
235
+ with open(test_dir / "api_tests.yaml", "w") as f:
236
+ yaml.dump(api_test, f, default_flow_style=False)
@@ -0,0 +1 @@
1
+ """Data management modules"""
@@ -0,0 +1,367 @@
1
+ """
2
+ Local Database - SQLite-based local storage for all nextOG data
3
+ Privacy-first: All data stays on the user's machine
4
+ """
5
+
6
+ import sqlite3
7
+ import json
8
+ from typing import Dict, List, Optional, Any
9
+ from pathlib import Path
10
+ from datetime import datetime
11
+ from rich.console import Console
12
+
13
+ console = Console()
14
+
15
+
16
+ class LocalDatabase:
17
+ """Local SQLite database for nextOG CLI"""
18
+
19
+ def __init__(self, db_path: str = None):
20
+ if db_path is None:
21
+ db_dir = Path.home() / ".nextog" / "data"
22
+ db_dir.mkdir(parents=True, exist_ok=True)
23
+ db_path = str(db_dir / "nextog.db")
24
+
25
+ self.db_path = db_path
26
+ self._init_db()
27
+
28
+ def _get_conn(self) -> sqlite3.Connection:
29
+ """Get database connection"""
30
+ conn = sqlite3.connect(self.db_path)
31
+ conn.row_factory = sqlite3.Row
32
+ return conn
33
+
34
+ def _init_db(self):
35
+ """Initialize database tables"""
36
+ conn = self._get_conn()
37
+
38
+ try:
39
+ conn.executescript("""
40
+ -- Test results storage
41
+ CREATE TABLE IF NOT EXISTS test_results (
42
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
43
+ engine TEXT NOT NULL,
44
+ project TEXT,
45
+ results_json TEXT NOT NULL,
46
+ coverage REAL DEFAULT 0,
47
+ timestamp TEXT NOT NULL,
48
+ duration REAL DEFAULT 0
49
+ );
50
+
51
+ -- User accounts
52
+ CREATE TABLE IF NOT EXISTS users (
53
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
54
+ username TEXT UNIQUE NOT NULL,
55
+ role TEXT NOT NULL DEFAULT 'tester',
56
+ email TEXT,
57
+ status TEXT DEFAULT 'active',
58
+ created_at TEXT NOT NULL,
59
+ last_login TEXT
60
+ );
61
+
62
+ -- Learned patterns
63
+ CREATE TABLE IF NOT EXISTS patterns (
64
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
65
+ pattern_type TEXT NOT NULL,
66
+ frequency INTEGER DEFAULT 0,
67
+ context TEXT,
68
+ related_elements TEXT,
69
+ timestamp TEXT NOT NULL
70
+ );
71
+
72
+ -- Detected elements
73
+ CREATE TABLE IF NOT EXISTS elements (
74
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
75
+ target TEXT NOT NULL,
76
+ element_json TEXT NOT NULL,
77
+ timestamp TEXT NOT NULL
78
+ );
79
+
80
+ -- Metadata storage
81
+ CREATE TABLE IF NOT EXISTS metadata (
82
+ key TEXT PRIMARY KEY,
83
+ value TEXT NOT NULL,
84
+ updated_at TEXT NOT NULL
85
+ );
86
+
87
+ -- Optimized test order
88
+ CREATE TABLE IF NOT EXISTS optimized_tests (
89
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
90
+ test_name TEXT NOT NULL,
91
+ priority INTEGER DEFAULT 0,
92
+ engine TEXT,
93
+ timestamp TEXT NOT NULL
94
+ );
95
+
96
+ -- Activity log
97
+ CREATE TABLE IF NOT EXISTS activity_log (
98
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
99
+ activity_type TEXT NOT NULL,
100
+ data_json TEXT,
101
+ timestamp TEXT NOT NULL
102
+ );
103
+
104
+ -- Coverage tracking
105
+ CREATE TABLE IF NOT EXISTS coverage_history (
106
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
107
+ project TEXT,
108
+ coverage REAL NOT NULL,
109
+ phase TEXT,
110
+ timestamp TEXT NOT NULL
111
+ );
112
+ """)
113
+
114
+ conn.commit()
115
+ except Exception as e:
116
+ console.print(f"[red]Database init error: {e}[/red]")
117
+ finally:
118
+ conn.close()
119
+
120
+ # ─── Test Results ──────────────────────────────────────────────
121
+
122
+ def save_test_results(self, results: Dict):
123
+ """Save test results"""
124
+ conn = self._get_conn()
125
+ try:
126
+ conn.execute(
127
+ "INSERT INTO test_results (engine, project, results_json, coverage, timestamp, duration) VALUES (?, ?, ?, ?, ?, ?)",
128
+ (
129
+ results.get("engine", "unknown"),
130
+ results.get("project", ""),
131
+ json.dumps(results.get("results", results), default=str),
132
+ results.get("results", {}).get("coverage", 0) if isinstance(results.get("results"), dict) else 0,
133
+ results.get("timestamp", datetime.now().isoformat()),
134
+ results.get("duration", 0),
135
+ )
136
+ )
137
+ conn.commit()
138
+ finally:
139
+ conn.close()
140
+
141
+ def get_latest_results(self, project: str = None) -> Optional[Dict]:
142
+ """Get latest test results"""
143
+ conn = self._get_conn()
144
+ try:
145
+ if project:
146
+ row = conn.execute(
147
+ "SELECT * FROM test_results WHERE project = ? ORDER BY timestamp DESC LIMIT 1",
148
+ (project,)
149
+ ).fetchone()
150
+ else:
151
+ row = conn.execute(
152
+ "SELECT * FROM test_results ORDER BY timestamp DESC LIMIT 1"
153
+ ).fetchone()
154
+
155
+ if row:
156
+ return {
157
+ "id": row["id"],
158
+ "engine": row["engine"],
159
+ "project": row["project"],
160
+ "results": json.loads(row["results_json"]),
161
+ "coverage": row["coverage"],
162
+ "timestamp": row["timestamp"],
163
+ }
164
+ finally:
165
+ conn.close()
166
+ return None
167
+
168
+ def get_all_test_results(self) -> List[Dict]:
169
+ """Get all test results"""
170
+ conn = self._get_conn()
171
+ try:
172
+ rows = conn.execute("SELECT * FROM test_results ORDER BY timestamp DESC").fetchall()
173
+ return [
174
+ {
175
+ "id": row["id"],
176
+ "engine": row["engine"],
177
+ "project": row["project"],
178
+ "results": json.loads(row["results_json"]),
179
+ "coverage": row["coverage"],
180
+ "timestamp": row["timestamp"],
181
+ }
182
+ for row in rows
183
+ ]
184
+ finally:
185
+ conn.close()
186
+
187
+ def count_test_results(self) -> int:
188
+ """Count test results"""
189
+ conn = self._get_conn()
190
+ try:
191
+ row = conn.execute("SELECT COUNT(*) FROM test_results").fetchone()
192
+ return row[0]
193
+ finally:
194
+ conn.close()
195
+
196
+ # ─── Users ──────────────────────────────────────────────────────
197
+
198
+ def save_user(self, user: Dict):
199
+ """Save a user"""
200
+ conn = self._get_conn()
201
+ try:
202
+ conn.execute(
203
+ "INSERT OR REPLACE INTO users (username, role, email, status, created_at) VALUES (?, ?, ?, ?, ?)",
204
+ (user["username"], user["role"], user.get("email"), user["status"], user["created_at"])
205
+ )
206
+ conn.commit()
207
+ finally:
208
+ conn.close()
209
+
210
+ def get_user(self, username: str) -> Optional[Dict]:
211
+ """Get user by username"""
212
+ conn = self._get_conn()
213
+ try:
214
+ row = conn.execute("SELECT * FROM users WHERE username = ?", (username,)).fetchone()
215
+ if row:
216
+ return dict(row)
217
+ finally:
218
+ conn.close()
219
+ return None
220
+
221
+ def get_all_users(self) -> List[Dict]:
222
+ """Get all users"""
223
+ conn = self._get_conn()
224
+ try:
225
+ rows = conn.execute("SELECT * FROM users").fetchall()
226
+ return [dict(row) for row in rows]
227
+ finally:
228
+ conn.close()
229
+
230
+ def update_last_login(self, username: str):
231
+ """Update user's last login"""
232
+ conn = self._get_conn()
233
+ try:
234
+ conn.execute(
235
+ "UPDATE users SET last_login = ? WHERE username = ?",
236
+ (datetime.now().isoformat(), username)
237
+ )
238
+ conn.commit()
239
+ finally:
240
+ conn.close()
241
+
242
+ def update_user_role(self, username: str, role: str):
243
+ """Update user's role"""
244
+ conn = self._get_conn()
245
+ try:
246
+ conn.execute("UPDATE users SET role = ? WHERE username = ?", (role, username))
247
+ conn.commit()
248
+ finally:
249
+ conn.close()
250
+
251
+ def delete_user(self, username: str):
252
+ """Delete a user"""
253
+ conn = self._get_conn()
254
+ try:
255
+ conn.execute("DELETE FROM users WHERE username = ?", (username,))
256
+ conn.commit()
257
+ finally:
258
+ conn.close()
259
+
260
+ # ─── Patterns ──────────────────────────────────────────────────
261
+
262
+ def save_pattern(self, pattern: Dict):
263
+ """Save a learned pattern"""
264
+ conn = self._get_conn()
265
+ try:
266
+ conn.execute(
267
+ "INSERT INTO patterns (pattern_type, frequency, context, related_elements, timestamp) VALUES (?, ?, ?, ?, ?)",
268
+ (
269
+ pattern["type"],
270
+ pattern["frequency"],
271
+ pattern.get("context", ""),
272
+ json.dumps(pattern.get("related_elements", [])),
273
+ pattern.get("timestamp", datetime.now().isoformat()),
274
+ )
275
+ )
276
+ conn.commit()
277
+ finally:
278
+ conn.close()
279
+
280
+ def count_patterns(self) -> int:
281
+ """Count stored patterns"""
282
+ conn = self._get_conn()
283
+ try:
284
+ row = conn.execute("SELECT COUNT(*) FROM patterns").fetchone()
285
+ return row[0]
286
+ finally:
287
+ conn.close()
288
+
289
+ # ─── Elements ──────────────────────────────────────────────────
290
+
291
+ def save_elements(self, target: str, elements: List[Dict]):
292
+ """Save detected elements"""
293
+ conn = self._get_conn()
294
+ try:
295
+ for elem in elements:
296
+ conn.execute(
297
+ "INSERT INTO elements (target, element_json, timestamp) VALUES (?, ?, ?)",
298
+ (target, json.dumps(elem, default=str), datetime.now().isoformat())
299
+ )
300
+ conn.commit()
301
+ finally:
302
+ conn.close()
303
+
304
+ # ─── Metadata ──────────────────────────────────────────────────
305
+
306
+ def save_metadata(self, key: str, value: Any):
307
+ """Save metadata"""
308
+ conn = self._get_conn()
309
+ try:
310
+ conn.execute(
311
+ "INSERT OR REPLACE INTO metadata (key, value, updated_at) VALUES (?, ?, ?)",
312
+ (key, json.dumps(value, default=str), datetime.now().isoformat())
313
+ )
314
+ conn.commit()
315
+ finally:
316
+ conn.close()
317
+
318
+ def get_metadata(self, key: str) -> Any:
319
+ """Get metadata value"""
320
+ conn = self._get_conn()
321
+ try:
322
+ row = conn.execute("SELECT value FROM metadata WHERE key = ?", (key,)).fetchone()
323
+ if row:
324
+ return json.loads(row[0])
325
+ finally:
326
+ conn.close()
327
+ return None
328
+
329
+ # ─── Optimized Tests ───────────────────────────────────────────
330
+
331
+ def count_optimized_tests(self) -> int:
332
+ """Count optimized tests"""
333
+ conn = self._get_conn()
334
+ try:
335
+ row = conn.execute("SELECT COUNT(*) FROM optimized_tests").fetchone()
336
+ return row[0]
337
+ finally:
338
+ conn.close()
339
+
340
+ # ─── Export/Purge ──────────────────────────────────────────────
341
+
342
+ def export_all(self) -> Dict:
343
+ """Export all data"""
344
+ conn = self._get_conn()
345
+ try:
346
+ data = {
347
+ "test_results": [dict(r) for r in conn.execute("SELECT * FROM test_results").fetchall()],
348
+ "users": [dict(r) for r in conn.execute("SELECT * FROM users").fetchall()],
349
+ "patterns": [dict(r) for r in conn.execute("SELECT * FROM patterns").fetchall()],
350
+ "elements": [dict(r) for r in conn.execute("SELECT * FROM elements").fetchall()],
351
+ "metadata": {r["key"]: json.loads(r["value"]) for r in conn.execute("SELECT * FROM metadata").fetchall()},
352
+ "exported_at": datetime.now().isoformat(),
353
+ }
354
+ return data
355
+ finally:
356
+ conn.close()
357
+
358
+ def purge_all(self):
359
+ """Delete all data"""
360
+ conn = self._get_conn()
361
+ try:
362
+ tables = ["test_results", "users", "patterns", "elements", "metadata", "optimized_tests", "activity_log", "coverage_history"]
363
+ for table in tables:
364
+ conn.execute(f"DELETE FROM {table}")
365
+ conn.commit()
366
+ finally:
367
+ conn.close()
nextog/data/models.py ADDED
@@ -0,0 +1,72 @@
1
+ """Data models using Pydantic for validation"""
2
+
3
+ from pydantic import BaseModel, Field
4
+ from typing import List, Optional, Dict, Any
5
+ from datetime import datetime
6
+
7
+
8
+ class TestResult(BaseModel):
9
+ engine: str
10
+ total: int = 0
11
+ passed: int = 0
12
+ failed: int = 0
13
+ skipped: int = 0
14
+ coverage: float = 0.0
15
+ duration: float = 0.0
16
+ timestamp: str = Field(default_factory=lambda: datetime.now().isoformat())
17
+ details: List[Dict[str, Any]] = []
18
+
19
+
20
+ class TestCase(BaseModel):
21
+ name: str
22
+ test_type: str
23
+ element: str = ""
24
+ description: str = ""
25
+ priority: str = "medium"
26
+ steps: List[Dict[str, Any]] = []
27
+ expected_result: str = ""
28
+ actual_result: str = ""
29
+ status: str = "pending"
30
+
31
+
32
+ class User(BaseModel):
33
+ username: str
34
+ role: str = "tester"
35
+ email: Optional[str] = None
36
+ status: str = "active"
37
+ created_at: str = Field(default_factory=lambda: datetime.now().isoformat())
38
+
39
+
40
+ class Pattern(BaseModel):
41
+ type: str
42
+ frequency: int = 0
43
+ context: str = ""
44
+ related_elements: List[str] = []
45
+ timestamp: str = Field(default_factory=lambda: datetime.now().isoformat())
46
+
47
+
48
+ class Element(BaseModel):
49
+ name: str
50
+ type: str
51
+ selector: str = ""
52
+ description: str = ""
53
+ priority: str = "medium"
54
+ visible: bool = True
55
+ attributes: Dict[str, str] = {}
56
+
57
+
58
+ class ProjectConfig(BaseModel):
59
+ project: str
60
+ version: str = "1.0"
61
+ engines: Dict[str, Any] = {}
62
+ coverage_target: int = 90
63
+ parallel: bool = True
64
+
65
+
66
+ class CoverageData(BaseModel):
67
+ total_coverage: float = 0.0
68
+ phases: Dict[str, Any] = {}
69
+ elements_covered: int = 0
70
+ elements_total: int = 0
71
+ timestamp: str = Field(default_factory=lambda: datetime.now().isoformat())
72
+ project: Optional[str] = None