gcharanteja commited on
Commit
419fce7
·
1 Parent(s): 3d4271b

Enhance README and API functionality with new endpoints and error handling; add package.json and package-lock.json for Node.js testing

Browse files
Files changed (6) hide show
  1. .gitignore +3 -0
  2. README.md +156 -10
  3. app.py +71 -15
  4. package-lock.json +106 -0
  5. package.json +14 -0
  6. test.js +226 -0
.gitignore ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ /venv
2
+
3
+ /node_modules
README.md CHANGED
@@ -1,10 +1,156 @@
1
- ---
2
- title: SqlLIte
3
- emoji: 🦀
4
- colorFrom: gray
5
- colorTo: green
6
- sdk: docker
7
- pinned: false
8
- ---
9
-
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SQLite Server on HF Spaces
2
+
3
+ A persistent SQLite database server running on Hugging Face Spaces with FastAPI wrapper.
4
+
5
+ ## Features
6
+
7
+ ✅ **Persistent Storage** - Data stored in `/data/sqlite.db` (survives restarts)
8
+ ✅ **REST API** - Full SQLite CRUD operations via HTTP
9
+ ✅ **Dual Services** - SQLite server (port 8000) + FastAPI wrapper (port 7860)
10
+ ✅ **Schema Management** - Query schema and list tables
11
+ ✅ **HF Spaces Ready** - Docker setup for Hugging Face deployment
12
+
13
+ ## Architecture
14
+
15
+ ```
16
+ ┌─────────────────────────────────────────────┐
17
+ │ HF Spaces Container │
18
+ ├─────────────────────────────────────────────┤
19
+ │ Port 7860 (exposed) │
20
+ │ ├── FastAPI Wrapper (app.py) │
21
+ │ │ └── Proxies to SQLite Server │
22
+ │ │ │
23
+ │ Port 8000 (internal) │
24
+ │ └── SQLite Server (sqlite_server.py) │
25
+ │ └── /data/sqlite.db (persistent) │
26
+ └─────────────────────────────────────────────┘
27
+ ```
28
+
29
+ ## API Endpoints
30
+
31
+ ### Health Check
32
+ ```bash
33
+ GET /health
34
+ GET /api/v1/heartbeat
35
+ ```
36
+
37
+ ### Query Data (SELECT)
38
+ ```bash
39
+ POST /api/v1/query
40
+ Content-Type: application/json
41
+
42
+ {
43
+ "sql": "SELECT * FROM users"
44
+ }
45
+ ```
46
+
47
+ ### Execute (INSERT/UPDATE/DELETE)
48
+ ```bash
49
+ POST /api/v1/execute
50
+ Content-Type: application/json
51
+
52
+ {
53
+ "sql": "INSERT INTO users (name, email) VALUES ('Alice', 'alice@example.com')"
54
+ }
55
+ ```
56
+
57
+ ### Schema & Tables
58
+ ```bash
59
+ GET /api/v1/tables
60
+ GET /api/v1/schema
61
+ ```
62
+
63
+ ## Deployment to HF Spaces
64
+
65
+ 1. **Create a new Space:**
66
+ - Go to https://huggingface.co/new-space
67
+ - Select "Docker" as SDK
68
+ - Upload/push this repo
69
+
70
+ 2. **Files needed:**
71
+ - `Dockerfile` - Container setup
72
+ - `start.sh` - Startup script
73
+ - `app.py` - FastAPI wrapper
74
+ - `sqlite_server.py` - SQLite API server
75
+ - `requirements.txt` - Python dependencies
76
+
77
+ 3. **Persistent Storage:**
78
+ - HF Spaces automatically mounts `/data` as persistent volume
79
+ - SQLite database is stored at `/data/sqlite.db`
80
+ - Data persists across Space restarts
81
+
82
+ ## Local Testing
83
+
84
+ ```bash
85
+ # Install dependencies
86
+ npm install
87
+ pip install -r requirements.txt
88
+
89
+ # Start the servers
90
+ source venv/bin/activate
91
+ python3 sqlite_server.py & # Port 8000
92
+ python3 app.py & # Port 7860
93
+
94
+ # Run tests
95
+ npm run test
96
+ ```
97
+
98
+ ## File Structure
99
+
100
+ ```
101
+ .
102
+ ├── app.py # FastAPI wrapper (port 7860)
103
+ ├── sqlite_server.py # SQLite API server (port 8000)
104
+ ├── start.sh # Startup script
105
+ ├── Dockerfile # Container configuration
106
+ ├── requirements.txt # Python dependencies
107
+ ├── package.json # Node.js test config
108
+ ├── test.js # Test suite
109
+ └── README.md # This file
110
+ ```
111
+
112
+ ## Example Usage
113
+
114
+ ### Create a table
115
+ ```bash
116
+ curl -X POST https://your-space.hf.space/api/v1/execute \
117
+ -H "Content-Type: application/json" \
118
+ -d '{
119
+ "sql": "CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)"
120
+ }'
121
+ ```
122
+
123
+ ### Insert data
124
+ ```bash
125
+ curl -X POST https://your-space.hf.space/api/v1/execute \
126
+ -H "Content-Type: application/json" \
127
+ -d '{
128
+ "sql": "INSERT INTO users (name, email) VALUES ('"'"'Alice'"'"', '"'"'alice@example.com'"'"')"
129
+ }'
130
+ ```
131
+
132
+ ### Query data
133
+ ```bash
134
+ curl -X POST https://your-space.hf.space/api/v1/query \
135
+ -H "Content-Type: application/json" \
136
+ -d '{"sql": "SELECT * FROM users"}'
137
+ ```
138
+
139
+ ## Troubleshooting
140
+
141
+ **Health check fails:**
142
+ - Ensure SQLite server started before FastAPI wrapper
143
+ - Check `start.sh` sleep duration (currently 2 seconds)
144
+
145
+ **Port conflicts:**
146
+ - SQLite server: port 8000 (internal)
147
+ - FastAPI wrapper: port 7860 (exposed on HF Spaces)
148
+
149
+ **Data not persisting:**
150
+ - Verify `/data` directory exists
151
+ - Check HF Spaces has persistent storage enabled
152
+ - Database file should be at `/data/sqlite.db`
153
+
154
+ ## Credits
155
+
156
+ Pattern based on ChromaDB HF Spaces deployment.
app.py CHANGED
@@ -1,12 +1,19 @@
1
  import os
2
  import requests
3
- from fastapi import FastAPI
4
- from fastapi.responses import HTMLResponse
 
5
 
6
  app = FastAPI(title="SQLite on HF Space")
7
 
8
  SQLITE_URL = "http://localhost:8000"
9
 
 
 
 
 
 
 
10
  @app.get("/", response_class=HTMLResponse)
11
  def read_root():
12
  return """
@@ -14,32 +21,81 @@ def read_root():
14
  <head><title>SQLite HF Space</title></head>
15
  <body>
16
  <h1>✅ SQLite Server is Running!</h1>
17
- <p>Persistent Storage: /data/sqlite.db</p>
18
- <p>API Endpoint: <a href="/docs">Swagger UI</a></p>
19
- <p>Direct SQLite API: <code>{}/api/v1/heartbeat</code></p>
20
- <p><strong>Available Endpoints:</strong></p>
21
  <ul>
22
- <li>GET /api/v1/heartbeat - Health check</li>
23
- <li>POST /api/v1/query - Execute SELECT queries</li>
24
- <li>POST /api/v1/execute - Execute INSERT/UPDATE/DELETE</li>
25
- <li>GET /api/v1/tables - List all tables</li>
26
- <li>GET /api/v1/schema - Get database schema</li>
27
  </ul>
28
  </body>
29
  </html>
30
- """.format(SQLITE_URL)
31
 
32
  @app.get("/health")
33
  def health_check():
34
  try:
35
- response = requests.get(f"{SQLITE_URL}/api/v1/heartbeat")
36
  if response.status_code == 200:
37
  return {"status": "healthy", "sqlite": "up"}
38
  else:
39
- return {"status": "unhealthy", "sqlite": "error"}
40
  except Exception as e:
41
  return {"status": "error", "detail": str(e)}
42
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  if __name__ == "__main__":
44
  import uvicorn
45
- uvicorn.run(app, host="0.0.0.0", port=int(os.getenv("PORT", 7860)))
 
 
1
  import os
2
  import requests
3
+ from fastapi import FastAPI, HTTPException, Request
4
+ from fastapi.responses import HTMLResponse, JSONResponse
5
+ from pydantic import BaseModel
6
 
7
  app = FastAPI(title="SQLite on HF Space")
8
 
9
  SQLITE_URL = "http://localhost:8000"
10
 
11
+ class QueryRequest(BaseModel):
12
+ sql: str
13
+
14
+ class ExecuteRequest(BaseModel):
15
+ sql: str
16
+
17
  @app.get("/", response_class=HTMLResponse)
18
  def read_root():
19
  return """
 
21
  <head><title>SQLite HF Space</title></head>
22
  <body>
23
  <h1>✅ SQLite Server is Running!</h1>
24
+ <p><strong>Persistent Storage:</strong> /data/sqlite.db</p>
25
+ <p><strong>API Documentation:</strong> <a href="/docs">Swagger UI</a></p>
26
+ <hr>
27
+ <h3>Available Endpoints:</h3>
28
  <ul>
29
+ <li><code>GET /api/v1/heartbeat</code> - Health check</li>
30
+ <li><code>POST /api/v1/query</code> - Execute SELECT queries</li>
31
+ <li><code>POST /api/v1/execute</code> - Execute INSERT/UPDATE/DELETE</li>
32
+ <li><code>GET /api/v1/tables</code> - List all tables</li>
33
+ <li><code>GET /api/v1/schema</code> - Get database schema</li>
34
  </ul>
35
  </body>
36
  </html>
37
+ """
38
 
39
  @app.get("/health")
40
  def health_check():
41
  try:
42
+ response = requests.get(f"{SQLITE_URL}/api/v1/heartbeat", timeout=5)
43
  if response.status_code == 200:
44
  return {"status": "healthy", "sqlite": "up"}
45
  else:
46
+ return {"status": "unhealthy", "detail": response.text}
47
  except Exception as e:
48
  return {"status": "error", "detail": str(e)}
49
 
50
+ @app.get("/api/v1/heartbeat")
51
+ def heartbeat():
52
+ try:
53
+ response = requests.get(f"{SQLITE_URL}/api/v1/heartbeat", timeout=5)
54
+ return response.json()
55
+ except Exception as e:
56
+ raise HTTPException(status_code=500, detail=f"SQLite server error: {str(e)}")
57
+
58
+ @app.post("/api/v1/query")
59
+ def query(request: QueryRequest):
60
+ try:
61
+ response = requests.post(f"{SQLITE_URL}/api/v1/query", json={"sql": request.sql}, timeout=10)
62
+ if response.status_code != 200:
63
+ raise HTTPException(status_code=response.status_code, detail=response.text)
64
+ return response.json()
65
+ except Exception as e:
66
+ raise HTTPException(status_code=500, detail=f"Query error: {str(e)}")
67
+
68
+ @app.post("/api/v1/execute")
69
+ def execute(request: ExecuteRequest):
70
+ try:
71
+ response = requests.post(f"{SQLITE_URL}/api/v1/execute", json={"sql": request.sql}, timeout=10)
72
+ if response.status_code != 200:
73
+ raise HTTPException(status_code=response.status_code, detail=response.text)
74
+ return response.json()
75
+ except Exception as e:
76
+ raise HTTPException(status_code=500, detail=f"Execute error: {str(e)}")
77
+
78
+ @app.get("/api/v1/tables")
79
+ def list_tables():
80
+ try:
81
+ response = requests.get(f"{SQLITE_URL}/api/v1/tables", timeout=5)
82
+ if response.status_code != 200:
83
+ raise HTTPException(status_code=response.status_code, detail=response.text)
84
+ return response.json()
85
+ except Exception as e:
86
+ raise HTTPException(status_code=500, detail=f"Tables error: {str(e)}")
87
+
88
+ @app.get("/api/v1/schema")
89
+ def get_schema():
90
+ try:
91
+ response = requests.get(f"{SQLITE_URL}/api/v1/schema", timeout=5)
92
+ if response.status_code != 200:
93
+ raise HTTPException(status_code=response.status_code, detail=response.text)
94
+ return response.json()
95
+ except Exception as e:
96
+ raise HTTPException(status_code=500, detail=f"Schema error: {str(e)}")
97
+
98
  if __name__ == "__main__":
99
  import uvicorn
100
+ port = int(os.getenv("PORT", 7860))
101
+ uvicorn.run(app, host="0.0.0.0", port=port)
package-lock.json ADDED
@@ -0,0 +1,106 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "sqlite-server-tester",
3
+ "version": "1.0.0",
4
+ "lockfileVersion": 3,
5
+ "requires": true,
6
+ "packages": {
7
+ "": {
8
+ "name": "sqlite-server-tester",
9
+ "version": "1.0.0",
10
+ "dependencies": {
11
+ "node-fetch": "^3.3.0"
12
+ }
13
+ },
14
+ "node_modules/data-uri-to-buffer": {
15
+ "version": "4.0.1",
16
+ "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
17
+ "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==",
18
+ "license": "MIT",
19
+ "engines": {
20
+ "node": ">= 12"
21
+ }
22
+ },
23
+ "node_modules/fetch-blob": {
24
+ "version": "3.2.0",
25
+ "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
26
+ "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
27
+ "funding": [
28
+ {
29
+ "type": "github",
30
+ "url": "https://github.com/sponsors/jimmywarting"
31
+ },
32
+ {
33
+ "type": "paypal",
34
+ "url": "https://paypal.me/jimmywarting"
35
+ }
36
+ ],
37
+ "license": "MIT",
38
+ "dependencies": {
39
+ "node-domexception": "^1.0.0",
40
+ "web-streams-polyfill": "^3.0.3"
41
+ },
42
+ "engines": {
43
+ "node": "^12.20 || >= 14.13"
44
+ }
45
+ },
46
+ "node_modules/formdata-polyfill": {
47
+ "version": "4.0.10",
48
+ "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
49
+ "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
50
+ "license": "MIT",
51
+ "dependencies": {
52
+ "fetch-blob": "^3.1.2"
53
+ },
54
+ "engines": {
55
+ "node": ">=12.20.0"
56
+ }
57
+ },
58
+ "node_modules/node-domexception": {
59
+ "version": "1.0.0",
60
+ "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
61
+ "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
62
+ "deprecated": "Use your platform's native DOMException instead",
63
+ "funding": [
64
+ {
65
+ "type": "github",
66
+ "url": "https://github.com/sponsors/jimmywarting"
67
+ },
68
+ {
69
+ "type": "github",
70
+ "url": "https://paypal.me/jimmywarting"
71
+ }
72
+ ],
73
+ "license": "MIT",
74
+ "engines": {
75
+ "node": ">=10.5.0"
76
+ }
77
+ },
78
+ "node_modules/node-fetch": {
79
+ "version": "3.3.2",
80
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz",
81
+ "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==",
82
+ "license": "MIT",
83
+ "dependencies": {
84
+ "data-uri-to-buffer": "^4.0.0",
85
+ "fetch-blob": "^3.1.4",
86
+ "formdata-polyfill": "^4.0.10"
87
+ },
88
+ "engines": {
89
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
90
+ },
91
+ "funding": {
92
+ "type": "opencollective",
93
+ "url": "https://opencollective.com/node-fetch"
94
+ }
95
+ },
96
+ "node_modules/web-streams-polyfill": {
97
+ "version": "3.3.3",
98
+ "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz",
99
+ "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==",
100
+ "license": "MIT",
101
+ "engines": {
102
+ "node": ">= 8"
103
+ }
104
+ }
105
+ }
106
+ }
package.json ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "sqlite-server-tester",
3
+ "version": "1.0.0",
4
+ "description": "Test suite for SQLite Server",
5
+ "main": "test.js",
6
+ "type": "module",
7
+ "scripts": {
8
+ "test": "node test.js",
9
+ "dev": "node test.js"
10
+ },
11
+ "dependencies": {
12
+ "node-fetch": "^3.3.0"
13
+ }
14
+ }
test.js ADDED
@@ -0,0 +1,226 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const fetch = (...args) =>
2
+ import("node-fetch").then(({ default: fetch }) => fetch(...args));
3
+
4
+ // ============ CONFIG ============
5
+ // Change BASE_URL to your hosted server
6
+ const BASE_URL = "https://maxxcarl-sqllite.hf.space";
7
+ // For local testing, use: "http://localhost:7860"
8
+
9
+ // ============ COLORS FOR CONSOLE ============
10
+ const colors = {
11
+ reset: "\x1b[0m",
12
+ bright: "\x1b[1m",
13
+ green: "\x1b[32m",
14
+ blue: "\x1b[34m",
15
+ yellow: "\x1b[33m",
16
+ red: "\x1b[31m",
17
+ cyan: "\x1b[36m",
18
+ };
19
+
20
+ function log(msg, color = "reset") {
21
+ console.log(`${colors[color]}${msg}${colors.reset}`);
22
+ }
23
+
24
+ function header(msg) {
25
+ log(`\n${"=".repeat(60)}`, "cyan");
26
+ log(` ${msg}`, "bright");
27
+ log(`${"=".repeat(60)}\n`, "cyan");
28
+ }
29
+
30
+ // ============ API FUNCTIONS ============
31
+ async function apiCall(endpoint, method = "GET", body = null) {
32
+ const url = `${BASE_URL}${endpoint}`;
33
+ const options = {
34
+ method,
35
+ headers: { "Content-Type": "application/json" },
36
+ };
37
+
38
+ if (body) options.body = JSON.stringify(body);
39
+
40
+ const response = await fetch(url, options);
41
+ const data = await response.json();
42
+
43
+ if (!response.ok) {
44
+ throw new Error(data.detail || `API Error: ${response.status}`);
45
+ }
46
+
47
+ return data;
48
+ }
49
+
50
+ async function executeQuery(sql) {
51
+ return apiCall("/api/v1/query", "POST", { sql });
52
+ }
53
+
54
+ async function executeUpdate(sql) {
55
+ return apiCall("/api/v1/execute", "POST", { sql });
56
+ }
57
+
58
+ async function getTables() {
59
+ return apiCall("/api/v1/tables");
60
+ }
61
+
62
+ async function getSchema() {
63
+ return apiCall("/api/v1/schema");
64
+ }
65
+
66
+ async function healthCheck() {
67
+ return apiCall("/api/v1/heartbeat");
68
+ }
69
+
70
+ // ============ MAIN TEST FLOW ============
71
+ async function main() {
72
+ try {
73
+ log(`\n🚀 SQLite Server Tester`, "bright");
74
+ log(`Base URL: ${BASE_URL}`, "yellow");
75
+ log(`All endpoints accessed through: ${BASE_URL}\n`, "yellow");
76
+
77
+ // 1. Health Check
78
+ header("1️⃣ HEALTH CHECK");
79
+ try {
80
+ const health = await healthCheck();
81
+ log(`✅ Server is healthy!`, "green");
82
+ log(`Response: ${JSON.stringify(health, null, 2)}\n`, "cyan");
83
+ } catch (e) {
84
+ log(`❌ Server health check failed: ${e.message}`, "red");
85
+ return;
86
+ }
87
+
88
+ // 2. Create Users Table
89
+ header("2️⃣ CREATE USERS TABLE");
90
+ const createUsersSQL = `
91
+ CREATE TABLE IF NOT EXISTS users (
92
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
93
+ name TEXT NOT NULL,
94
+ email TEXT NOT NULL UNIQUE,
95
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP
96
+ )
97
+ `;
98
+ try {
99
+ await executeUpdate(createUsersSQL);
100
+ log(`✅ Users table created!`, "green");
101
+ } catch (e) {
102
+ log(`❌ Error creating users table: ${e.message}`, "red");
103
+ }
104
+
105
+ // 3. Create Products Table
106
+ header("3️⃣ CREATE PRODUCTS TABLE");
107
+ const createProductsSQL = `
108
+ CREATE TABLE IF NOT EXISTS products (
109
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
110
+ name TEXT NOT NULL,
111
+ price REAL NOT NULL,
112
+ stock INTEGER DEFAULT 0,
113
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP
114
+ )
115
+ `;
116
+ try {
117
+ await executeUpdate(createProductsSQL);
118
+ log(`✅ Products table created!`, "green");
119
+ } catch (e) {
120
+ log(`❌ Error creating products table: ${e.message}`, "red");
121
+ }
122
+
123
+ // 4. Insert Users
124
+ header("4️⃣ INSERT USERS");
125
+ const usersData = [
126
+ { name: "Alice Johnson", email: "alice@example.com" },
127
+ { name: "Bob Smith", email: "bob@example.com" },
128
+ { name: "Charlie Brown", email: "charlie@example.com" },
129
+ ];
130
+
131
+ for (const user of usersData) {
132
+ const insertSQL = `INSERT INTO users (name, email) VALUES ('${user.name}', '${user.email}')`;
133
+ try {
134
+ await executeUpdate(insertSQL);
135
+ log(` ✅ Added: ${user.name}`, "green");
136
+ } catch (e) {
137
+ log(` ⚠️ ${user.name}: ${e.message}`, "yellow");
138
+ }
139
+ }
140
+
141
+ // 5. Insert Products
142
+ header("5️⃣ INSERT PRODUCTS");
143
+ const productsData = [
144
+ { name: "Laptop Pro", price: 1299.99, stock: 15 },
145
+ { name: "Wireless Mouse", price: 29.99, stock: 100 },
146
+ { name: "USB-C Cable", price: 9.99, stock: 250 },
147
+ { name: "Monitor 4K", price: 499.99, stock: 8 },
148
+ ];
149
+
150
+ for (const product of productsData) {
151
+ const insertSQL = `INSERT INTO products (name, price, stock) VALUES ('${product.name}', ${product.price}, ${product.stock})`;
152
+ try {
153
+ await executeUpdate(insertSQL);
154
+ log(` ✅ Added: ${product.name}`, "green");
155
+ } catch (e) {
156
+ log(` ⚠️ ${product.name}: ${e.message}`, "yellow");
157
+ }
158
+ }
159
+
160
+ // 6. Get All Tables
161
+ header("6️⃣ LIST ALL TABLES");
162
+ try {
163
+ const tables = await getTables();
164
+ log(`Found tables: ${tables.tables.join(", ")}`, "cyan");
165
+ } catch (e) {
166
+ log(`❌ Error getting tables: ${e.message}`, "red");
167
+ }
168
+
169
+ // 7. Get Schema
170
+ header("7️⃣ DATABASE SCHEMA");
171
+ try {
172
+ const schema = await getSchema();
173
+ log(JSON.stringify(schema, null, 2), "cyan");
174
+ } catch (e) {
175
+ log(`❌ Error getting schema: ${e.message}`, "red");
176
+ }
177
+
178
+ // 8. Query Users
179
+ header("8️⃣ QUERY USERS");
180
+ try {
181
+ const result = await executeQuery("SELECT * FROM users");
182
+ log(JSON.stringify(result.data, null, 2), "cyan");
183
+ } catch (e) {
184
+ log(`❌ Error querying users: ${e.message}`, "red");
185
+ }
186
+
187
+ // 9. Query Products with Price > 50
188
+ header("9️⃣ QUERY EXPENSIVE PRODUCTS (> $50)");
189
+ try {
190
+ const result = await executeQuery(
191
+ "SELECT * FROM products WHERE price > 50 ORDER BY price DESC",
192
+ );
193
+ log(JSON.stringify(result.data, null, 2), "cyan");
194
+ } catch (e) {
195
+ log(`❌ Error querying products: ${e.message}`, "red");
196
+ }
197
+
198
+ // 10. Summary
199
+ header("✅ ALL TESTS COMPLETED");
200
+ log(
201
+ `
202
+ 🎉 Database Setup Complete!
203
+
204
+ Tables Created:
205
+ - users (name, email, created_at)
206
+ - products (name, price, stock, created_at)
207
+
208
+ Sample Data:
209
+ - 3 users inserted
210
+ - 4 products inserted
211
+
212
+ Access your data:
213
+ - API: ${BASE_URL}
214
+ - Swagger UI: ${BASE_URL}/docs
215
+ - Health: ${BASE_URL}/health
216
+ `,
217
+ "green",
218
+ );
219
+ } catch (error) {
220
+ log(`\n❌ Fatal Error: ${error.message}`, "red");
221
+ process.exit(1);
222
+ }
223
+ }
224
+
225
+ // Run the tests
226
+ main().catch(console.error);