/testsql - SQL Server Test Environment Manager
Manages a Docker-based SQL Server 2022 test environment for YourProject script development and testing.
User Documentation
Commands
/testsql setup [scriptname] [sql|azuresql] # Setup SQL Server with optional script and engine type /testsql backup # Backup current database to bootstrap folder /testsql restore <filename> # Restore database from backup file /testsql shutdown # Backup and teardown container /testsql status # Show connection information and system status /testsql help # Show this help message
Engine Types
- •sql (default): SQL Server 2022 - Full feature set, best for YourProject testing
- •azuresql: Azure SQL Edge - Lightweight, optimized for edge/IoT scenarios
Quick Start
# Start SQL Server with Northwind sample database /testsql setup # Output: Connection details (server, database, username, password) # Work with database in SSMS... # Backup when done /testsql backup # Shutdown /testsql shutdown
Connection Information
After setup, you'll receive:
- •Server:
localhost,1433 - •Database:
testdb(or custom from script) - •Username:
sa - •Password:
YourSecurePassword123! - •Connection String: For SSMS/Azure Data Studio
Prerequisites
- •Docker Desktop (8GB+ RAM allocated)
- •120GB+ disk space
- •Windows/macOS/Linux
File Locations
- •Bootstrap scripts:
.claude/testSQL/bootstrap/*.sql - •Backups:
.claude/testSQL/bootstrap/*.bak - •Docker config:
.claude/testSQLSetup/
Implementation Instructions for Claude
When User Invokes: /testsql setup [scriptname] [sql|azuresql]
Step 1: Parse Command Arguments
Extract arguments and determine engine type:
Argument parsing logic:
Arguments: None → scriptname: northwind.sql → engine: sql Arguments: azuresql → scriptname: northwind.sql → engine: azuresql Arguments: myscript.sql → scriptname: myscript.sql → engine: sql Arguments: myscript.sql sql → scriptname: myscript.sql → engine: sql Arguments: myscript.sql azuresql → scriptname: myscript.sql → engine: azuresql
Detection rules:
- •If first argument is "azuresql" or "sql" → It's engine type, script is default (northwind.sql)
- •If first argument ends with .sql → It's script name, check second argument for engine
- •If second argument is "azuresql" or "sql" → Use that engine
- •Otherwise → Default to "sql" engine
Store variables:
- •
scriptname- Bootstrap script filename - •
engine- Engine type: "sql" or "azuresql" - •
containerName- Container name based on engine:- •sql →
sqlserver-dev - •azuresql →
azuresql-dev
- •sql →
- •
imageName- Docker image:- •sql →
mcr.microsoft.com/mssql/server:2022-latest - •azuresql →
mcr.microsoft.com/azure-sql-edge:latest
- •sql →
Step 2: Check Prerequisites
Use the Bash tool to verify:
# Check Docker installed docker --version # Check Docker running docker info # Check Docker Compose available docker compose version
If any check fails:
- •Output clear error message
- •Provide installation link: https://www.docker.com/products/docker-desktop
- •Stop execution
Step 3: Create Directories
Use Bash tool to create folders if missing:
mkdir -p .claude/testSQL/bootstrap mkdir -p .claude/testSQLSetup/data mkdir -p .claude/testSQLSetup/logs
Step 4: Ensure Bootstrap Script Exists
If using default (no script argument):
Check if northwind.sql exists:
test -f .claude/testSQL/bootstrap/northwind.sql && echo "exists" || echo "missing"
If missing, create it using Write tool:
File: .claude/testSQL/bootstrap/northwind.sql
Content:
-- Northwind Sample Database for Testing
-- Auto-generated by /testsql skill
CREATE DATABASE testdb;
GO
USE testdb;
GO
-- Customers table
CREATE TABLE Customers (
CustomerID INT PRIMARY KEY IDENTITY(1,1),
CompanyName NVARCHAR(100) NOT NULL,
ContactName NVARCHAR(100),
Country NVARCHAR(50),
CreatedDate DATETIME DEFAULT GETDATE()
);
-- Orders table
CREATE TABLE Orders (
OrderID INT PRIMARY KEY IDENTITY(1,1),
CustomerID INT FOREIGN KEY REFERENCES Customers(CustomerID),
OrderDate DATETIME DEFAULT GETDATE(),
TotalAmount DECIMAL(10,2)
);
-- Products table
CREATE TABLE Products (
ProductID INT PRIMARY KEY IDENTITY(1,1),
ProductName NVARCHAR(100) NOT NULL,
UnitPrice DECIMAL(10,2),
UnitsInStock INT DEFAULT 0
);
-- Sample data
INSERT INTO Customers (CompanyName, ContactName, Country) VALUES
('Contoso Ltd', 'John Doe', 'USA'),
('Fabrikam Inc', 'Jane Smith', 'Canada'),
('Adventure Works', 'Bob Johnson', 'UK'),
('Northwind Traders', 'Alice Williams', 'Germany');
INSERT INTO Products (ProductName, UnitPrice, UnitsInStock) VALUES
('Widget', 9.99, 100),
('Gadget', 19.99, 50),
('Doohickey', 14.99, 75),
('Thingamajig', 24.99, 30);
INSERT INTO Orders (CustomerID, OrderDate, TotalAmount) VALUES
(1, '2026-01-15', 1500.00),
(1, '2026-01-20', 2300.50),
(2, '2026-01-18', 750.25),
(3, '2026-01-22', 980.00);
PRINT 'Northwind sample database created successfully';
GO
If using custom script:
Check if custom script exists:
test -f .claude/testSQL/bootstrap/SCRIPTNAME && echo "exists" || echo "missing"
If missing:
- •Output error: "Script not found: .claude/testSQL/bootstrap/SCRIPTNAME"
- •Suggest: "Place your SQL script in .claude/testSQL/bootstrap/"
- •Stop execution
Step 5: Detect Database Name from Script
Use Read tool to read the bootstrap script content.
Parse the script to find database name:
Look for patterns (case-insensitive):
- •
CREATE DATABASE [DatabaseName] - •
CREATE DATABASE DatabaseName - •
CREATE DATABASE "DatabaseName" - •
CREATE DATABASE 'DatabaseName'
Extract database name:
- •If pattern found: Extract the database name (remove brackets/quotes)
- •If pattern NOT found: Use default
testdb
Example parsing logic:
Script contains: "CREATE DATABASE MyCustomDB;" → Extracted database name: MyCustomDB Script contains: "CREATE TABLE Users (...);" → No CREATE DATABASE found → Default database name: testdb
Store the detected database name in a variable for later use.
Step 6: Check Container Status
Use Bash to check if container exists and its state (use containerName from Step 1):
docker ps -a --filter "name=${containerName}" --format "{{.Names}}\t{{.Status}}"
Where containerName is:
- •
sqlserver-dev(for sql engine) - •
azuresql-dev(for azuresql engine)
Possible states:
- •
Container running:
- •Output: "Container already running (${containerName}). Checking connection..."
- •Skip to Step 9 (verify connection)
- •
Container stopped:
- •Output: "Container exists but stopped (${containerName}). Starting..."
- •Use:
docker start ${containerName} - •Continue to Step 9
- •
Container not found:
- •Output: "Creating new ${engine} container..."
- •Continue to Step 7
Additional check - Detect existing SQL containers:
If target container not found, check for ANY SQL container:
docker ps --format "{{.Names}}\t{{.Image}}" | grep -iE "sql|mssql"
If other SQL containers found:
- •List them: "Found existing SQL containers: container1, container2"
- •Output: "Creating new ${containerName} for this setup"
- •Continue to Step 7 (user can manually use other containers if needed)
Step 7: Start Docker Container
Use direct docker run command instead of docker-compose (simpler for multiple engine support):
For SQL Server (sql engine):
docker run -d \ --name sqlserver-dev \ -e "ACCEPT_EULA=Y" \ -e "SA_PASSWORD=YourSecurePassword123!" \ -e "MSSQL_PID=Developer" \ -e "MSSQL_AGENT_ENABLED=true" \ -p 1433:1433 \ -v "$(pwd)/.claude/testSQL/bootstrap:/var/opt/mssql/backup" \ -v "$(pwd)/.claude/testSQLSetup/data:/var/opt/mssql/data" \ -v "$(pwd)/.claude/testSQLSetup/logs:/var/opt/mssql/log" \ --memory=7g \ --cpus=4 \ mcr.microsoft.com/mssql/server:2022-latest
For Azure SQL Edge (azuresql engine):
docker run -d \ --name azuresql-dev \ -e "ACCEPT_EULA=Y" \ -e "SA_PASSWORD=YourSecurePassword123!" \ -e "MSSQL_PID=Developer" \ -p 1433:1433 \ -v "$(pwd)/.claude/testSQL/bootstrap:/var/opt/mssql/backup" \ -v "$(pwd)/.claude/testSQLSetup/data-azuresql:/var/opt/mssql/data" \ -v "$(pwd)/.claude/testSQLSetup/logs-azuresql:/var/opt/mssql/log" \ --memory=4g \ --cpus=2 \ mcr.microsoft.com/azure-sql-edge:latest
Note: Azure SQL Edge uses less memory (4GB vs 7GB) and fewer CPUs (2 vs 4).
If command fails:
- •Check if image exists:
docker images | grep ${imageName} - •If image missing: Pull it first
bash
docker pull ${imageName} - •Retry the docker run command
Output progress:
✓ Starting ${engine} container (${containerName})...
Pulling image if needed...
This may take 2-3 minutes on first run...
Step 8: Wait for SQL Server to be Ready
Poll SQL Server every 5 seconds for up to 3 minutes (use containerName from Step 1):
docker exec ${containerName} /opt/mssql-tools/bin/sqlcmd \
-S localhost -U sa -P 'YourSecurePassword123!' -Q "SELECT 1" 2>/dev/null
While waiting:
- •Show progress every 30 seconds: "Still waiting for ${engine}... (elapsed: 30s)"
- •If timeout (3 minutes):
- •Output error: "${engine} failed to start within 3 minutes"
- •Suggest: "Check logs with: docker logs ${containerName}"
- •Stop execution
When ready:
✓ ${engine} is ready!
Step 9: Run Bootstrap Script
Use containerName from Step 1.
If database name was NOT found in script (use testdb):
- •Create testdb first:
docker exec ${containerName} /opt/mssql-tools/bin/sqlcmd \
-S localhost -U sa -P 'YourSecurePassword123!' \
-Q "IF DB_ID('testdb') IS NULL CREATE DATABASE testdb;"
- •Run script in context of testdb:
docker cp .claude/testSQL/bootstrap/${scriptname} ${containerName}:/tmp/bootstrap.sql
docker exec ${containerName} /opt/mssql-tools/bin/sqlcmd \
-S localhost -U sa -P 'YourSecurePassword123!' -d testdb \
-i /tmp/bootstrap.sql
If database name WAS found in script:
Run script directly (it will create its own database):
docker cp .claude/testSQL/bootstrap/${scriptname} ${containerName}:/tmp/bootstrap.sql
docker exec ${containerName} /opt/mssql-tools/bin/sqlcmd \
-S localhost -U sa -P 'YourSecurePassword123!' \
-i /tmp/bootstrap.sql
If script execution fails:
- •Output error message
- •Show last 20 lines of output
- •Suggest: "Check script syntax in .claude/testSQL/bootstrap/${scriptname}"
Step 10: Display Connection Information
Output a nicely formatted connection info box:
======================================== SQL Server Connection Information ======================================== Engine Type: [SQL Server 2022 | Azure SQL Edge] Container: [CONTAINER_NAME] Server Name: localhost,1433 Database Name: [DETECTED_DATABASE_NAME] Username: sa Password: YourSecurePassword123! Connection String (SSMS/Azure Data Studio): Server=localhost,1433;Database=[DETECTED_DATABASE_NAME];User Id=sa;Password=YourSecurePassword123!;TrustServerCertificate=True; ======================================== Ready to connect! Open SSMS and use the credentials above. ========================================
Replace values:
- •
[SQL Server 2022 | Azure SQL Edge]with friendly engine name:- •sql → "SQL Server 2022"
- •azuresql → "Azure SQL Edge"
- •
[CONTAINER_NAME]with actual container name from Step 1 - •
[DETECTED_DATABASE_NAME]with actual database name from Step 5
When User Invokes: /testsql backup
Step 1: Detect and Check Running Container
Find any running SQL container:
docker ps --format "{{.Names}}\t{{.Image}}" | grep -iE "dams.*sql"
Expected matches:
- •
sqlserver-dev(SQL Server) - •
azuresql-dev(Azure SQL Edge)
If multiple containers running:
- •Use the first one found
- •Output: "Using container: ${containerName}"
If no container running:
- •Output: "No SQL container running. Start with: /testsql setup"
- •Stop execution
Store containerName for subsequent steps.
Step 2: Detect Current Database Name
Query SQL Server to get the first user database (use containerName from Step 1):
docker exec ${containerName} /opt/mssql-tools/bin/sqlcmd \
-S localhost -U sa -P 'YourSecurePassword123!' -h -1 \
-Q "SET NOCOUNT ON; SELECT TOP 1 name FROM sys.databases WHERE database_id > 4 ORDER BY database_id"
Store database name (e.g., testdb, MyCustomDB, etc.)
If no user database found:
- •Output: "No user database found to backup"
- •Stop execution
Step 3: Generate Timestamp
Create timestamp in format: YYYYMMDD_HHMMSS
Example: 20260129_143022
Step 4: Create Backup
Backup filename: [DATABASE_NAME]_[TIMESTAMP].bak
Use containerName from Step 1:
docker exec ${containerName} /opt/mssql-tools/bin/sqlcmd \
-S localhost -U sa -P 'YourSecurePassword123!' \
-Q "BACKUP DATABASE [DATABASE_NAME] TO DISK='/var/opt/mssql/backup/[DATABASE_NAME]_[TIMESTAMP].bak' WITH COMPRESSION, INIT;"
Replace [DATABASE_NAME] and [TIMESTAMP] with actual values.
If backup fails:
- •Output error message
- •Check disk space:
df -h .claude/testSQL/bootstrap - •Suggest deleting old backups
Step 5: Get Backup Size
ls -lh .claude/testSQL/bootstrap/[DATABASE_NAME]_[TIMESTAMP].bak | awk '{print $5}'
Step 6: Display Success Message
✓ Backup completed successfully Backup Details: File: [DATABASE_NAME]_[TIMESTAMP].bak Location: .claude/testSQL/bootstrap/ Size: [SIZE] MB (compressed) Timestamp: [HUMAN_READABLE_TIME]
When User Invokes: /testsql restore <filename>
Step 1: Parse Command Arguments
Extract filename:
- •If no argument: Show error and list available backups (interactive mode)
- •If argument provided: Use that filename
Step 2: Detect and Check Running Container
Find any running SQL container (same logic as backup Step 1):
docker ps --format "{{.Names}}\t{{.Image}}" | grep -iE "dams.*sql"
If no container running:
- •Output: "No SQL container running. Start with: /testsql setup"
- •Stop execution
Store containerName for subsequent steps.
Step 3: Verify Backup File Exists
test -f .claude/testSQL/bootstrap/FILENAME && echo "exists" || echo "missing"
If missing:
- •Output: "Backup file not found: FILENAME"
- •List available backups:
ls -lht .claude/testSQL/bootstrap/*.bak | head -10
- •Stop execution
Step 4: Extract Database Name from Backup Filename
Parse filename to extract database name.
Pattern: DBNAME_YYYYMMDD_HHMMSS.bak
Example:
- •
testdb_20260129_143022.bak→ Database:testdb - •
MyCustomDB_20260128_091534.bak→ Database:MyCustomDB
Step 5: Close Active Connections
Kill all connections to the target database (use containerName from Step 2):
docker exec ${containerName} /opt/mssql-tools/bin/sqlcmd \
-S localhost -U sa -P 'YourSecurePassword123!' \
-Q "ALTER DATABASE [DBNAME] SET SINGLE_USER WITH ROLLBACK IMMEDIATE;"
If fails (database doesn't exist):
- •This is OK, continue (restore will create it)
Step 6: Restore Database
docker exec ${containerName} /opt/mssql-tools/bin/sqlcmd \
-S localhost -U sa -P 'YourSecurePassword123!' \
-Q "RESTORE DATABASE [DBNAME] FROM DISK='/var/opt/mssql/backup/FILENAME' WITH REPLACE;"
If restore fails:
- •Output error message
- •Show SQL Server error details
- •Suggest checking backup file integrity
Step 7: Set Database to Multi-User
docker exec ${containerName} /opt/mssql-tools/bin/sqlcmd \
-S localhost -U sa -P 'YourSecurePassword123!' \
-Q "ALTER DATABASE [DBNAME] SET MULTI_USER;"
Step 8: Get Database Size
docker exec ${containerName} /opt/mssql-tools/bin/sqlcmd \
-S localhost -U sa -P 'YourSecurePassword123!' -h -1 \
-Q "SET NOCOUNT ON; SELECT CAST(SUM(size) * 8.0 / 1024 AS DECIMAL(10,2)) FROM sys.master_files WHERE database_id = DB_ID('[DBNAME]');"
Step 9: Display Success Message
✓ Database restored successfully Restore Details: Database: [DBNAME] Size: [SIZE] MB Source: FILENAME Restored: [CURRENT_TIMESTAMP]
When User Invokes: /testsql shutdown
Step 1: Detect and Check Running Container
Find any running SQL container (same logic as backup Step 1):
docker ps --format "{{.Names}}\t{{.Image}}" | grep -iE "dams.*sql"
If no container running:
- •Output: "No SQL container running. Nothing to shutdown."
- •Stop execution
Store containerName for subsequent steps.
Step 2: Auto-Backup First
Output: "Creating automatic backup before shutdown..."
Follow the same logic as /testsql backup:
- •Detect database name
- •Generate timestamp
- •Create backup
- •Show backup details
If backup fails:
- •Ask user: "Backup failed. Continue shutdown anyway? (yes/no)"
- •If no: Stop execution
- •If yes: Continue to Step 3
Step 3: Stop Container
Use containerName from Step 1:
docker stop ${containerName}
Output:
✓ Stopping container (${containerName})...
✓ Container stopped successfully
Step 4: Remove Container (Keep Data)
docker rm ${containerName}
Note: This removes container but keeps Docker volumes (data persists in .claude/testSQLSetup/data* folders)
Step 5: Display Summary
======================================== Shutdown Complete ======================================== ✓ Backup created: [FILENAME] ✓ Container stopped and removed ✓ Data volumes preserved ✓ Port 1433 released ✓ Memory freed: ~7 GB Next startup will be fast (~30 seconds) Run: /testsql setup ========================================
When User Invokes: /testsql status
Step 1: Detect All SQL Containers
Find all SQL containers (running or stopped):
docker ps -a --format "{{.Names}}\t{{.Image}}\t{{.Status}}\t{{.RunningFor}}" | grep -iE "dams.*sql"
Store all found containers. If multiple found, show status for all.
Parse output for each:
- •If "Up": Container running
- •If "Exited": Container stopped
Use the first running container for detailed stats, or the most recently used stopped container.
Step 2: Get Container Stats (if running)
Use containerName from Step 1:
docker stats ${containerName} --no-stream --format "{{.CPUPerc}}\t{{.MemUsage}}\t{{.NetIO}}\t{{.BlockIO}}"
Step 3: Detect Engine Type
Based on container name or image:
- •
sqlserver-dev→ "SQL Server 2022" - •
azuresql-dev→ "Azure SQL Edge"
Step 4: Test SQL Server Connection (if running)
docker exec ${containerName} /opt/mssql-tools/bin/sqlcmd \
-S localhost -U sa -P 'YourSecurePassword123!' \
-Q "SELECT @@VERSION" 2>/dev/null
If successful: Extract SQL Server version
Step 5: Get Database Name (if running)
docker exec ${containerName} /opt/mssql-tools/bin/sqlcmd \
-S localhost -U sa -P 'YourSecurePassword123!' -h -1 \
-Q "SET NOCOUNT ON; SELECT TOP 1 name FROM sys.databases WHERE database_id > 4"
Step 6: List Available Backups
ls -lht .claude/testSQL/bootstrap/*.bak 2>/dev/null | head -5
Parse output to show filename, size, timestamp
Step 7: Display Status Report
If container running:
======================================== SQL Server Status ======================================== Engine Type: [SQL Server 2022 | Azure SQL Edge] Container: [CONTAINER_NAME] Status: ✓ Running Uptime: [UPTIME] CPU Usage: [CPU]% Memory Usage: [MEM_USED] / [MEM_TOTAL] GB Network I/O: [NET_RX] / [NET_TX] Disk I/O: [DISK_READ] / [DISK_WRITE] SQL Server Version: [VERSION] Connection: ✓ Active ======================================== Connection Information ======================================== Server Name: localhost,1433 Database Name: [DBNAME] Username: sa Password: YourSecurePassword123! Connection String (SSMS/Azure Data Studio): Server=localhost,1433;Database=[DBNAME];User Id=sa;Password=YourSecurePassword123!;TrustServerCertificate=True; ======================================== Available Backups ======================================== [BACKUP_LIST with sizes and timestamps] Total: [COUNT] backups ========================================
Replace values:
- •
[SQL Server 2022 | Azure SQL Edge]with detected engine type from Step 3 - •
[CONTAINER_NAME]with actual container name from Step 1
If container not running:
======================================== SQL Server Status ======================================== Container Status: ✗ Not Running To start SQL Server, run: /testsql setup ======================================== Available Backups ======================================== [BACKUP_LIST] ========================================
When User Invokes: /testsql help
Display the user documentation section (commands, usage, prerequisites).
Error Handling Guidelines
Common Errors to Handle Gracefully:
- •
Docker not installed:
- •Clear message: "Docker is not installed"
- •Provide link: https://www.docker.com/products/docker-desktop
- •Exit cleanly
- •
Docker not running:
- •Clear message: "Docker is not running"
- •Suggest: "Start Docker Desktop and try again"
- •Exit cleanly
- •
Port 1433 in use:
- •Detect:
netstat -an | grep :1433orlsof -i :1433 - •Clear message: "Port 1433 is already in use"
- •Suggest: "Stop other SQL Server instances or change port in .env"
- •Detect:
- •
Insufficient disk space:
- •Detect:
df -h .claude/testSQL/bootstrap - •Clear message: "Insufficient disk space"
- •Suggest: "Delete old backups or free up space"
- •Detect:
- •
Script syntax error:
- •Show SQL error message
- •Suggest: "Check script syntax in .claude/testSQL/bootstrap/[SCRIPT]"
- •Offer to continue with default northwind.sql
- •
Container startup timeout:
- •Clear message: "SQL Server failed to start within 3 minutes"
- •Suggest: "Check logs: docker logs sqlserver-dev"
- •Suggest: "Try rebuilding: docker compose build --no-cache"
Output Formatting Guidelines
Use Clear Visual Separators:
======================================== Section Title ======================================== Content here... ========================================
Use Status Indicators:
- •Success:
✓ Operation completed - •Error:
✗ Operation failed - •Warning:
⚠ Warning message - •Info:
ℹ Information
Use Progress Indicators:
✓ Step 1 completed ✓ Step 2 completed ⋯ Step 3 in progress...
Use Code Blocks for Commands:
When suggesting commands to user:
To check logs, run: ```bash docker logs sqlserver-dev ```
Testing Recommendations
Before finalizing, test these scenarios:
- •✓ First-time setup (no Docker image)
- •✓ Setup with default Northwind
- •✓ Setup with custom script (with CREATE DATABASE)
- •✓ Setup with custom script (without CREATE DATABASE)
- •✓ Backup and restore workflow
- •✓ Shutdown and restart
- •✓ Error handling (Docker not running, script error, etc.)
Version: 2.0 (Agent-Based) Created: 2026-01-29 Type: Agent-Based Skill (no .sh file needed) Maintained by: YourProject Team