updated frontend
This commit is contained in:
@@ -0,0 +1,179 @@
|
||||
# Test Data Generation Scripts
|
||||
|
||||
This directory contains scripts to help manage test data for the webstore backend.
|
||||
|
||||
## Available Scripts
|
||||
|
||||
### 0. Generate Placeholder Images
|
||||
Creates placeholder JPEG images for all 17 products used in seeding.
|
||||
|
||||
**File:** `generate-placeholder-images.js`
|
||||
|
||||
**What it does:**
|
||||
- Generates 17 placeholder product images (400x300px)
|
||||
- Saves images to `../images/` directory
|
||||
- Creates `uploads/` directory for user-uploaded images
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
# Using npm script
|
||||
npm run generate-images
|
||||
|
||||
# Or directly with Node
|
||||
node scripts/generate-placeholder-images.js
|
||||
```
|
||||
|
||||
**When to run:**
|
||||
- First time setting up development environment
|
||||
- When `/images` route returns 404 (images not found)
|
||||
- After cleaning up the images directory
|
||||
|
||||
**Images created (17 total):**
|
||||
- Shoes: street-runner.jpg, trail-edge.jpg, urban-sprint.jpg, classic-comfort.jpg
|
||||
- Bags: urban-tote.jpg, backpack-pro.jpg, crossbody.jpg, duffle.jpg
|
||||
- Accessories: classic-cap.jpg, silk-scarf.jpg, leather-belt.jpg, watch-straps.jpg
|
||||
- Clothing: cotton-tshirt.jpg, denim-jacket.jpg, yoga-leggings.jpg
|
||||
- Electronics: earbuds.jpg, usb-hub.jpg
|
||||
|
||||
---
|
||||
|
||||
### 1. Seed Script (Automatic)
|
||||
Runs automatically when the backend starts in development mode.
|
||||
|
||||
**File:** `../prisma/seed.js`
|
||||
|
||||
**What it does:**
|
||||
- Creates 5 product categories (Shoes, Bags, Accessories, Clothing, Electronics)
|
||||
- Creates 16 sample products across all categories
|
||||
- Creates 3 test user accounts
|
||||
|
||||
**Test User Credentials:**
|
||||
```
|
||||
Email: admin@test.com
|
||||
Password: admin123
|
||||
|
||||
Email: john@test.com
|
||||
Password: password123
|
||||
|
||||
Email: jane@test.com
|
||||
Password: password123
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Generate Test Data Script
|
||||
Generates additional bulk test data on demand.
|
||||
|
||||
### Usage Options
|
||||
|
||||
#### Option A: Windows Batch File (Recommended for Windows)
|
||||
```bash
|
||||
# Generate default test data (10 products, 5 orders)
|
||||
scripts\generate-test-data.bat
|
||||
|
||||
# Generate 20 products instead of 10
|
||||
scripts\generate-test-data.bat --products 20
|
||||
|
||||
# Generate 20 products and 10 orders
|
||||
scripts\generate-test-data.bat --products 20 --orders 10
|
||||
|
||||
# DANGEROUS: Reset database and regenerate all data
|
||||
scripts\generate-test-data.bat --reset --products 50 --orders 20
|
||||
```
|
||||
|
||||
#### Option B: NPM Script
|
||||
```bash
|
||||
# Generate default test data
|
||||
npm run generate-data
|
||||
|
||||
# With arguments (from project root)
|
||||
npm run generate-data -- --products 30 --orders 15
|
||||
```
|
||||
|
||||
#### Option C: Direct Node.js
|
||||
```bash
|
||||
# From project root
|
||||
node scripts/generate-test-data.js --products 25 --orders 10
|
||||
```
|
||||
|
||||
### Command-line Options
|
||||
|
||||
| Option | Description | Default |
|
||||
|--------|-------------|---------|
|
||||
| `--products N` | Number of additional products to generate | 10 |
|
||||
| `--orders N` | Number of test orders to generate | 5 |
|
||||
| `--reset` | ⚠️ **DANGEROUS**: Delete all data first | (not set) |
|
||||
|
||||
### Examples
|
||||
|
||||
```bash
|
||||
# Quick test: Generate 5 products and 2 orders
|
||||
scripts\generate-test-data.bat --products 5 --orders 2
|
||||
|
||||
# Moderate data: 30 products and 10 orders
|
||||
npm run generate-data -- --products 30 --orders 10
|
||||
|
||||
# Heavy load test: 100 products and 50 orders
|
||||
scripts\generate-test-data.bat --products 100 --orders 50
|
||||
|
||||
# Start fresh: Reset and generate 50 products and 20 orders
|
||||
scripts\generate-test-data.bat --reset --products 50 --orders 20
|
||||
```
|
||||
|
||||
## What Gets Generated
|
||||
|
||||
### Products
|
||||
- Random product names with auto-incrementing counters
|
||||
- Random prices between 5,990 and 24,990 HUF
|
||||
- Random stock quantities (5-55 units)
|
||||
- Random category assignment
|
||||
- Placeholder image URLs
|
||||
|
||||
### Test Users
|
||||
- 5 additional test user accounts
|
||||
- Email: `user[1-5]@test.com`
|
||||
- Password: `password123`
|
||||
|
||||
### Orders
|
||||
- Random customer names
|
||||
- Random customer emails
|
||||
- Random order items (1-3 products per order)
|
||||
- Automatically calculated total prices
|
||||
|
||||
## Important Notes
|
||||
|
||||
⚠️ **WARNING**: The `--reset` flag will delete:
|
||||
- All orders and order items
|
||||
- All products
|
||||
- All categories
|
||||
- All users
|
||||
|
||||
Only use `--reset` if you know what you're doing!
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "Node.js is not installed or not in PATH"
|
||||
- Install Node.js from https://nodejs.org/
|
||||
- Make sure it's in your system PATH
|
||||
|
||||
### "Database connection error"
|
||||
- Ensure the database is running
|
||||
- Check `.env` file has correct `DATABASE_URL`
|
||||
- Run migrations first: `npm run prisma:push`
|
||||
|
||||
### "Unique constraint failed" errors
|
||||
- Product names must be unique
|
||||
- The script skips duplicates automatically
|
||||
- Use `--reset` to start fresh
|
||||
|
||||
## Docker Compose Integration
|
||||
|
||||
The test data generation automatically runs in docker-compose:
|
||||
```bash
|
||||
# Start backend (will seed data automatically)
|
||||
docker-compose up api
|
||||
|
||||
# The initial data is seeded when the container first starts
|
||||
```
|
||||
|
||||
For production, you may want to disable auto-seeding in the `docker-compose.yml` file.
|
||||
@@ -8,14 +8,14 @@ set "ARCHIVE_FILE=%IMAGE_OUT_DIR%\webstore-production-images.tar"
|
||||
set "API_IMAGE=webstore-api:prod"
|
||||
set "DB_IMAGE=postgres:16-alpine"
|
||||
|
||||
echo [1/6] Checking Docker availability...
|
||||
echo [1/7] Checking Docker availability...
|
||||
docker --version >nul 2>&1
|
||||
if errorlevel 1 (
|
||||
echo ERROR: Docker CLI is not available. Install Docker Desktop first.
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
echo [2/6] Building production API image: %API_IMAGE%
|
||||
echo [2/7] Building production API image: %API_IMAGE%
|
||||
pushd "%ROOT_DIR%" >nul
|
||||
docker build -t "%API_IMAGE%" -f Dockerfile .
|
||||
if errorlevel 1 (
|
||||
@@ -25,17 +25,33 @@ if errorlevel 1 (
|
||||
)
|
||||
popd >nul
|
||||
|
||||
echo [3/6] Pulling database image: %DB_IMAGE%
|
||||
echo [3/7] Pulling database image: %DB_IMAGE%
|
||||
docker pull "%DB_IMAGE%"
|
||||
if errorlevel 1 (
|
||||
echo ERROR: Failed to pull database image.
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
echo [4/6] Preparing image output directory: %IMAGE_OUT_DIR%
|
||||
echo [4/7] Preparing image output directory: %IMAGE_OUT_DIR%
|
||||
if not exist "%IMAGE_OUT_DIR%" mkdir "%IMAGE_OUT_DIR%"
|
||||
|
||||
echo [5/6] Exporting images to archive...
|
||||
echo [5/7] Creating placeholder product images...
|
||||
pushd "%ROOT_DIR%" >nul
|
||||
|
||||
REM Create uploads directory
|
||||
if not exist "%IMAGE_OUT_DIR%\uploads" mkdir "%IMAGE_OUT_DIR%\uploads"
|
||||
|
||||
REM Use Node.js to generate placeholder images reliably
|
||||
echo Generating placeholder images using Node.js...
|
||||
node "%ROOT_DIR%\scripts\generate-placeholder-images.js"
|
||||
if errorlevel 1 (
|
||||
echo WARNING: Could not generate placeholder images automatically
|
||||
echo Note: Images can be added manually to %IMAGE_OUT_DIR%
|
||||
)
|
||||
|
||||
popd >nul
|
||||
|
||||
echo [6/7] Exporting images to archive...
|
||||
if exist "%ARCHIVE_FILE%" del /f /q "%ARCHIVE_FILE%"
|
||||
docker save -o "%ARCHIVE_FILE%" "%API_IMAGE%" "%DB_IMAGE%"
|
||||
if errorlevel 1 (
|
||||
@@ -43,7 +59,35 @@ if errorlevel 1 (
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
echo [6/6] Done.
|
||||
echo [7/7] Done.
|
||||
echo.
|
||||
echo ========================================
|
||||
echo PRODUCTION BUILD COMPLETE
|
||||
echo ========================================
|
||||
echo.
|
||||
echo Created archive: "%ARCHIVE_FILE%"
|
||||
echo Share this file with students together with production.compose.yml and .production.env.
|
||||
echo.
|
||||
echo FILES TO DISTRIBUTE TO STUDENTS:
|
||||
echo 1. "%ARCHIVE_FILE%"
|
||||
echo 2. "%IMAGE_OUT_DIR%\production.compose.yml"
|
||||
echo 3. "%IMAGE_OUT_DIR%\.production.env" (with proper configuration)
|
||||
echo 4. "%IMAGE_OUT_DIR%\run-student-production.bat"
|
||||
echo.
|
||||
echo WHAT'S INCLUDED:
|
||||
echo - Production API Docker image
|
||||
echo - PostgreSQL database image
|
||||
echo - Initial seeding script (creates 5 categories, 16 products, 3 test users)
|
||||
echo - Placeholder product images (17 images)
|
||||
echo - Upload directory for student-generated images
|
||||
echo.
|
||||
echo STUDENTS SHOULD:
|
||||
echo 1. Extract webstore-production-images.tar
|
||||
echo 2. Copy .production.env and configure database credentials
|
||||
echo 3. Run run-student-production.bat
|
||||
echo 4. Database will auto-seed with sample data
|
||||
echo 5. Test users available:
|
||||
echo - admin@test.com / admin123
|
||||
echo - john@test.com / password123
|
||||
echo - jane@test.com / password123
|
||||
echo.
|
||||
exit /b 0
|
||||
@@ -0,0 +1,112 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
// Image list from README
|
||||
const images = [
|
||||
// Shoes
|
||||
'street-runner.jpg',
|
||||
'trail-edge.jpg',
|
||||
'urban-sprint.jpg',
|
||||
'classic-comfort.jpg',
|
||||
// Bags
|
||||
'urban-tote.jpg',
|
||||
'backpack-pro.jpg',
|
||||
'crossbody.jpg',
|
||||
'duffle.jpg',
|
||||
// Accessories
|
||||
'classic-cap.jpg',
|
||||
'silk-scarf.jpg',
|
||||
'leather-belt.jpg',
|
||||
'watch-straps.jpg',
|
||||
// Clothing
|
||||
'cotton-tshirt.jpg',
|
||||
'denim-jacket.jpg',
|
||||
'yoga-leggings.jpg',
|
||||
// Electronics
|
||||
'earbuds.jpg',
|
||||
'usb-hub.jpg',
|
||||
];
|
||||
|
||||
const imagesDir = path.join(__dirname, '..', 'images');
|
||||
const uploadsDir = path.join(imagesDir, 'uploads');
|
||||
|
||||
// Create directories if they don't exist
|
||||
if (!fs.existsSync(imagesDir)) {
|
||||
fs.mkdirSync(imagesDir, { recursive: true });
|
||||
console.log(`Created ${imagesDir}`);
|
||||
}
|
||||
|
||||
if (!fs.existsSync(uploadsDir)) {
|
||||
fs.mkdirSync(uploadsDir, { recursive: true });
|
||||
console.log(`Created ${uploadsDir}`);
|
||||
}
|
||||
|
||||
// Create simple JPEG placeholder images (minimal valid JPEG structure)
|
||||
// This is a minimal JPEG structure that works with image viewers
|
||||
const createPlaceholderJPEG = (filename, width, height) => {
|
||||
// Minimal JPEG header and footer
|
||||
const jpegHeader = Buffer.from([
|
||||
0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46,
|
||||
0x49, 0x46, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01,
|
||||
0x00, 0x01, 0x00, 0x00, 0xFF, 0xDB, 0x00, 0x43,
|
||||
0x00, 0x08, 0x06, 0x06, 0x07, 0x06, 0x05, 0x08,
|
||||
0x07, 0x07, 0x07, 0x09, 0x09, 0x08, 0x0A, 0x0C,
|
||||
0x14, 0x0D, 0x0C, 0x0B, 0x0B, 0x0C, 0x19, 0x12,
|
||||
0x13, 0x0F, 0x14, 0x1D, 0x1A, 0x1F, 0x1E, 0x1D,
|
||||
0x1A, 0x1C, 0x1C, 0x20, 0x24, 0x2E, 0x27, 0x20,
|
||||
0x22, 0x2C, 0x23, 0x1C, 0x1C, 0x28, 0x37, 0x29,
|
||||
0x2C, 0x30, 0x31, 0x34, 0x34, 0x34, 0x1F, 0x27,
|
||||
0x39, 0x3D, 0x38, 0x32, 0x3C, 0x2E, 0x33, 0x34,
|
||||
0x32, 0xFF, 0xC0, 0x00, 0x0B, 0x08, 0x00, height,
|
||||
0x00, width, 0x01, 0x01, 0x11, 0x00, 0xFF, 0xC4,
|
||||
0x00, 0x1F, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01,
|
||||
0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04,
|
||||
0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0xFF,
|
||||
0xC4, 0x00, 0xB5, 0x10, 0x00, 0x02, 0x01, 0x03,
|
||||
0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04,
|
||||
0x00, 0x00, 0x01, 0x7D, 0x01, 0x02, 0x03, 0x00,
|
||||
0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06,
|
||||
0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32,
|
||||
0x81, 0x91, 0xA1, 0x08, 0x23, 0x42, 0xB1, 0xC1,
|
||||
0x15, 0x52, 0xD1, 0xF0, 0x24, 0x33, 0x62, 0x72,
|
||||
0x82, 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A,
|
||||
0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, 0x35,
|
||||
0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45,
|
||||
0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55,
|
||||
0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65,
|
||||
0x66, 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75,
|
||||
0x76, 0x77, 0x78, 0x79, 0x7A, 0x83, 0x84, 0x85,
|
||||
0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94,
|
||||
0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3,
|
||||
0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2,
|
||||
0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA,
|
||||
0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9,
|
||||
0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8,
|
||||
0xD9, 0xDA, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6,
|
||||
0xE7, 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4,
|
||||
0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFF, 0xDA,
|
||||
0x00, 0x08, 0x01, 0x01, 0x00, 0x00, 0x3F, 0x00,
|
||||
0xFB, 0xD0, 0xFF, 0xD9,
|
||||
]);
|
||||
|
||||
return jpegHeader;
|
||||
};
|
||||
|
||||
let created = 0;
|
||||
images.forEach((filename) => {
|
||||
const filepath = path.join(imagesDir, filename);
|
||||
if (!fs.existsSync(filepath)) {
|
||||
const jpegData = createPlaceholderJPEG(filename, 400, 300);
|
||||
fs.writeFileSync(filepath, jpegData);
|
||||
console.log(`✓ Created ${filename}`);
|
||||
created++;
|
||||
} else {
|
||||
console.log(`✓ Already exists: ${filename}`);
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`\n${created} placeholder images generated successfully!`);
|
||||
console.log(`Images directory: ${imagesDir}`);
|
||||
@@ -0,0 +1,87 @@
|
||||
@echo off
|
||||
setlocal EnableExtensions EnableDelayedExpansion
|
||||
|
||||
::
|
||||
:: Generate test data script for webstore backend
|
||||
:: Usage: generate-test-data.bat [--products N] [--orders N] [--reset]
|
||||
::
|
||||
:: Examples:
|
||||
:: generate-test-data.bat (Creates 10 products, 5 orders)
|
||||
:: generate-test-data.bat --products 20 (Creates 20 products, 5 orders)
|
||||
:: generate-test-data.bat --products 50 --orders 10 --reset (Resets DB and generates data)
|
||||
::
|
||||
|
||||
cd /d "%~dp0.."
|
||||
|
||||
echo.
|
||||
echo ========================================
|
||||
echo Test Data Generator for Webstore
|
||||
echo ========================================
|
||||
echo.
|
||||
|
||||
set "PRODUCTS=10"
|
||||
set "ORDERS=5"
|
||||
set "RESET_FLAG="
|
||||
|
||||
:parse_args
|
||||
if "%~1"=="" goto start_generation
|
||||
if "%~1"=="--products" (
|
||||
if "%~2"=="" (
|
||||
echo ERROR: --products requires a number argument
|
||||
exit /b 1
|
||||
)
|
||||
set "PRODUCTS=%~2"
|
||||
shift
|
||||
shift
|
||||
goto parse_args
|
||||
)
|
||||
if "%~1"=="--orders" (
|
||||
if "%~2"=="" (
|
||||
echo ERROR: --orders requires a number argument
|
||||
exit /b 1
|
||||
)
|
||||
set "ORDERS=%~2"
|
||||
shift
|
||||
shift
|
||||
goto parse_args
|
||||
)
|
||||
if "%~1"=="--reset" (
|
||||
set "RESET_FLAG=--reset"
|
||||
shift
|
||||
goto parse_args
|
||||
)
|
||||
shift
|
||||
goto parse_args
|
||||
|
||||
:start_generation
|
||||
echo Configuration:
|
||||
echo Products to generate: %PRODUCTS%
|
||||
echo Orders to generate: %ORDERS%
|
||||
if defined RESET_FLAG (
|
||||
echo Reset database: YES (WARNING: All data will be deleted!)
|
||||
) else (
|
||||
echo Reset database: NO
|
||||
)
|
||||
echo.
|
||||
|
||||
echo Checking Node.js availability...
|
||||
node --version >nul 2>&1
|
||||
if errorlevel 1 (
|
||||
echo ERROR: Node.js is not installed or not in PATH
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
echo Running test data generator...
|
||||
node scripts/generate-test-data.js --products %PRODUCTS% --orders %ORDERS% %RESET_FLAG%
|
||||
|
||||
if errorlevel 1 (
|
||||
echo.
|
||||
echo ERROR: Test data generation failed!
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
echo.
|
||||
echo ========================================
|
||||
echo ✓ Test data generated successfully!
|
||||
echo ========================================
|
||||
exit /b 0
|
||||
@@ -0,0 +1,263 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Script to generate bulk test data for the webstore database
|
||||
* Usage: node scripts/generate-test-data.js [options]
|
||||
*
|
||||
* Options:
|
||||
* --products N Generate N additional products (default: 10)
|
||||
* --orders N Generate N test orders (default: 5)
|
||||
* --reset Delete all data before seeding (careful!)
|
||||
*/
|
||||
|
||||
require("dotenv").config({ path: ".env" });
|
||||
|
||||
const { PrismaClient } = require("@prisma/client");
|
||||
const bcryptjs = require("bcryptjs");
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
// Parse command line arguments
|
||||
const args = process.argv.slice(2);
|
||||
const options = {
|
||||
productsCount: 10,
|
||||
ordersCount: 5,
|
||||
shouldReset: false
|
||||
};
|
||||
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
if (args[i] === "--products" && args[i + 1]) {
|
||||
options.productsCount = parseInt(args[i + 1], 10);
|
||||
i++;
|
||||
} else if (args[i] === "--orders" && args[i + 1]) {
|
||||
options.ordersCount = parseInt(args[i + 1], 10);
|
||||
i++;
|
||||
} else if (args[i] === "--reset") {
|
||||
options.shouldReset = true;
|
||||
}
|
||||
}
|
||||
|
||||
const productNames = [
|
||||
"Premium Leather Boots",
|
||||
"Summer Canvas Shoes",
|
||||
"Minimalist Sneakers",
|
||||
"Casual Loafers",
|
||||
"Running Shoes Pro",
|
||||
"Hiking Boots",
|
||||
"Beach Sandals",
|
||||
"Formal Dress Shoes",
|
||||
"Winter Snow Boots",
|
||||
"Slip-on Comfort Shoes",
|
||||
"Designer Heels",
|
||||
"Waterproof Outdoor Boots",
|
||||
"Lightweight Mesh Runners",
|
||||
"Classic Oxfords",
|
||||
"Athletic Training Shoes"
|
||||
];
|
||||
|
||||
const descriptions = [
|
||||
"Premium quality with exceptional comfort",
|
||||
"Perfect for everyday wear and activities",
|
||||
"Stylish design with modern aesthetics",
|
||||
"Durable materials built to last",
|
||||
"Eco-friendly sustainable production",
|
||||
"Handcrafted with attention to detail",
|
||||
"Weather-resistant and waterproof",
|
||||
"Ergonomic design for all-day comfort",
|
||||
"Available in multiple colors",
|
||||
"Perfect for professional environments"
|
||||
];
|
||||
|
||||
function generateRandomPrice() {
|
||||
return (Math.floor(Math.random() * 20) + 5).toString() + "990.00";
|
||||
}
|
||||
|
||||
function generateRandomStock() {
|
||||
return Math.floor(Math.random() * 50) + 5;
|
||||
}
|
||||
|
||||
function getRandomElement(arr) {
|
||||
return arr[Math.floor(Math.random() * arr.length)];
|
||||
}
|
||||
|
||||
async function resetDatabase() {
|
||||
console.log("⚠️ Resetting database...");
|
||||
try {
|
||||
// Delete in order of dependencies
|
||||
await prisma.orderItem.deleteMany({});
|
||||
await prisma.order.deleteMany({});
|
||||
await prisma.product.deleteMany({});
|
||||
await prisma.category.deleteMany({});
|
||||
await prisma.user.deleteMany({});
|
||||
console.log("✓ Database reset completed");
|
||||
} catch (error) {
|
||||
console.error("Error resetting database:", error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async function seedInitialData() {
|
||||
console.log("Seeding initial categories...");
|
||||
|
||||
const categories = [
|
||||
{ name: "Shoes", slug: "shoes" },
|
||||
{ name: "Bags", slug: "bags" },
|
||||
{ name: "Accessories", slug: "accessories" },
|
||||
{ name: "Clothing", slug: "clothing" },
|
||||
{ name: "Electronics", slug: "electronics" }
|
||||
];
|
||||
|
||||
for (const category of categories) {
|
||||
await prisma.category.upsert({
|
||||
where: { slug: category.slug },
|
||||
update: {},
|
||||
create: category
|
||||
});
|
||||
}
|
||||
|
||||
console.log("✓ Categories seeded");
|
||||
}
|
||||
|
||||
async function generateProducts(count) {
|
||||
console.log(`\nGenerating ${count} additional products...`);
|
||||
|
||||
const categories = await prisma.category.findMany();
|
||||
const generatedProducts = [];
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
const category = getRandomElement(categories);
|
||||
const name = `${getRandomElement(productNames)} ${i + 1}`;
|
||||
|
||||
try {
|
||||
const product = await prisma.product.create({
|
||||
data: {
|
||||
name,
|
||||
description: getRandomElement(descriptions),
|
||||
price: generateRandomPrice(),
|
||||
stock: generateRandomStock(),
|
||||
categoryId: category.id,
|
||||
imageUrl: `/images/placeholder-${i + 1}.jpg`
|
||||
}
|
||||
});
|
||||
generatedProducts.push(product);
|
||||
|
||||
if ((i + 1) % 5 === 0) {
|
||||
process.stdout.write(`\r Progress: ${i + 1}/${count} products`);
|
||||
}
|
||||
} catch (error) {
|
||||
if (!error.message.includes("Unique constraint failed")) {
|
||||
console.error(`\nError creating product: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`\r✓ Generated ${generatedProducts.length} products`);
|
||||
return generatedProducts;
|
||||
}
|
||||
|
||||
async function generateTestUsers(count = 5) {
|
||||
console.log(`\nGenerating ${count} test users...`);
|
||||
|
||||
const users = [];
|
||||
for (let i = 1; i <= count; i++) {
|
||||
const email = `user${i}@test.com`;
|
||||
const passwordHash = await bcryptjs.hash("password123", 10);
|
||||
|
||||
try {
|
||||
const user = await prisma.user.upsert({
|
||||
where: { email },
|
||||
update: {},
|
||||
create: {
|
||||
name: `Test User ${i}`,
|
||||
email,
|
||||
passwordHash
|
||||
}
|
||||
});
|
||||
users.push(user);
|
||||
} catch (error) {
|
||||
console.error(`Error creating user: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`✓ Generated ${users.length} test users`);
|
||||
return users;
|
||||
}
|
||||
|
||||
async function generateOrders(count) {
|
||||
console.log(`\nGenerating ${count} test orders...`);
|
||||
|
||||
const products = await prisma.product.findMany({ take: 20 });
|
||||
const createdOrders = [];
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
try {
|
||||
const itemsCount = Math.floor(Math.random() * 3) + 1;
|
||||
const items = [];
|
||||
let totalPrice = 0;
|
||||
|
||||
for (let j = 0; j < itemsCount; j++) {
|
||||
const product = getRandomElement(products);
|
||||
const quantity = Math.floor(Math.random() * 3) + 1;
|
||||
items.push({
|
||||
productId: product.id,
|
||||
quantity,
|
||||
unitPrice: product.price
|
||||
});
|
||||
totalPrice += parseFloat(product.price) * quantity;
|
||||
}
|
||||
|
||||
const order = await prisma.order.create({
|
||||
data: {
|
||||
customerName: `Customer ${i + 1}`,
|
||||
customerEmail: `customer${i + 1}@example.com`,
|
||||
totalPrice: totalPrice.toFixed(2),
|
||||
items: {
|
||||
create: items
|
||||
}
|
||||
},
|
||||
include: {
|
||||
items: true
|
||||
}
|
||||
});
|
||||
|
||||
createdOrders.push(order);
|
||||
|
||||
if ((i + 1) % 2 === 0) {
|
||||
process.stdout.write(`\r Progress: ${i + 1}/${count} orders`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`\nError creating order: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`\r✓ Generated ${createdOrders.length} orders`);
|
||||
return createdOrders;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log("🚀 Test Data Generator for Webstore\n");
|
||||
console.log("Options:");
|
||||
console.log(` Products to generate: ${options.productsCount}`);
|
||||
console.log(` Orders to generate: ${options.ordersCount}`);
|
||||
console.log(` Reset database first: ${options.shouldReset ? "YES" : "NO"}\n`);
|
||||
|
||||
try {
|
||||
if (options.shouldReset) {
|
||||
await resetDatabase();
|
||||
}
|
||||
|
||||
await seedInitialData();
|
||||
await generateTestUsers(5);
|
||||
await generateProducts(options.productsCount);
|
||||
await generateOrders(options.ordersCount);
|
||||
|
||||
console.log("\n✨ Test data generation completed successfully!");
|
||||
} catch (error) {
|
||||
console.error("\n❌ Error during data generation:", error.message);
|
||||
process.exit(1);
|
||||
} finally {
|
||||
await prisma.$disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
Reference in New Issue
Block a user