diff --git a/db/Dockerfile b/db/Dockerfile new file mode 100644 index 0000000..bc9d089 --- /dev/null +++ b/db/Dockerfile @@ -0,0 +1,13 @@ +# Use the official PostgreSQL image +FROM postgres:latest + +# Set environment variables (without password authentication) +ENV POSTGRES_USER=dbuser +ENV POSTGRES_DB=demo +ENV POSTGRES_HOST_AUTH_METHOD=trust + +# Copy initialization SQL script +COPY init.sql /docker-entrypoint-initdb.d/ + +# Expose PostgreSQL port +EXPOSE 5432 \ No newline at end of file diff --git a/db/README.md b/db/README.md new file mode 100644 index 0000000..62799aa --- /dev/null +++ b/db/README.md @@ -0,0 +1,71 @@ +# PostgreSQL Database for Test Genie + +This project is a dockerized PostgreSQL database that provides the backend storage for Test Genie, a test automation management system. The database contains tables for managing test suites, test cases, and test steps. + +## Project Structure + +```sh +db +├── Dockerfile # Docker definition of the PostgreSQL DB +├── init.sql # SQL scripts to create tables and set up database schema +└── README.md # Project documentation + +``` + +## Database Schema + +The database contains the following tables: + +1. **test_suites**: Stores test suite information + + - id, name, description, timestamps (created_at, updated_at, deleted_at) + +2. **test_cases**: Stores individual test cases + + - id, test_suite_id, description, timestamps + +3. **test_steps**: Stores test execution steps + - id, test_case_id, command, selector, value, length_value, contained_text, chain_option, is_chained, step_order, timestamps + +## Setup Instructions + +1. **Build the Docker image:** + +```sh +docker build -t test-genie-db . + +``` + +2. **Run the container:** + +```sh +docker run -d --name test-genie-postgres -p 5432:5432 test-genie-db + +``` + +## Usage + +Once the container is running, you can connect to the database using: + +```sh +psql -h localhost -U dbuser -d demo + +``` + +The database is configured with the following credentials: + +- Username: dbuser +- Database: demo +- Port: 5432 +- Authentication: trust (no password required) + +## Features + +- Automatic timestamp management for created_at and updated_at fields +- Soft delete support via deleted_at fields +- Optimized indexes for better query performance +- Automatic trigger-based updated_at timestamp updates + +## License + +This project is licensed under the MIT License. diff --git a/db/init.sql b/db/init.sql new file mode 100644 index 0000000..1794c3a --- /dev/null +++ b/db/init.sql @@ -0,0 +1,68 @@ +-- Create test_suites table +CREATE TABLE test_suites ( + id SERIAL PRIMARY KEY, + name VARCHAR(255) NOT NULL, + description TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + deleted_at TIMESTAMP WITH TIME ZONE +); + +-- Create test_cases table +CREATE TABLE test_cases ( + id SERIAL PRIMARY KEY, + test_suite_id INTEGER NOT NULL REFERENCES test_suites(id), + description TEXT NOT NULL, + created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + deleted_at TIMESTAMP WITH TIME ZONE +); + +-- Create test_steps table +CREATE TABLE test_steps ( + id SERIAL PRIMARY KEY, + test_case_id INTEGER NOT NULL REFERENCES test_cases(id), + command VARCHAR(255) NOT NULL, + selector TEXT, + value TEXT, + length_value INTEGER, + contained_text TEXT, + chain_option VARCHAR(50), + is_chained BOOLEAN DEFAULT false, + step_order INTEGER NOT NULL, + created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + deleted_at TIMESTAMP WITH TIME ZONE +); + +-- Create indexes for better query performance +CREATE INDEX idx_test_cases_suite_id ON test_cases(test_suite_id); +CREATE INDEX idx_test_steps_case_id ON test_steps(test_case_id); +CREATE INDEX idx_test_suites_deleted_at ON test_suites(deleted_at); +CREATE INDEX idx_test_cases_deleted_at ON test_cases(deleted_at); +CREATE INDEX idx_test_steps_deleted_at ON test_steps(deleted_at); + +-- Create function to update updated_at timestamp +CREATE OR REPLACE FUNCTION update_updated_at_column() +RETURNS TRIGGER AS $$ +BEGIN + NEW.updated_at = CURRENT_TIMESTAMP; + RETURN NEW; +END; +$$ language 'plpgsql'; + +-- Create triggers to automatically update updated_at +CREATE TRIGGER update_test_suites_updated_at + BEFORE UPDATE ON test_suites + FOR EACH ROW + EXECUTE FUNCTION update_updated_at_column(); + +CREATE TRIGGER update_test_cases_updated_at + BEFORE UPDATE ON test_cases + FOR EACH ROW + EXECUTE FUNCTION update_updated_at_column(); + +CREATE TRIGGER update_test_steps_updated_at + BEFORE UPDATE ON test_steps + FOR EACH ROW + EXECUTE FUNCTION update_updated_at_column(); diff --git a/server/Dockerfile b/server/Dockerfile index c3b1d01..89f96e4 100644 --- a/server/Dockerfile +++ b/server/Dockerfile @@ -23,4 +23,4 @@ COPY . . EXPOSE 3003 # Define the command to run your app using CMD which defines your runtime -CMD ["node", "index.js"] \ No newline at end of file +CMD ["npm", "run", "dev"] \ No newline at end of file diff --git a/server/package-lock.json b/server/package-lock.json index 7de364c..bf282e5 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -14,6 +14,7 @@ "cors": "^2.8.5", "cypress": "^14.2.1", "express": "^4.18.2", + "pg": "^8.14.1", "uuid": "^9.0.0" }, "devDependencies": { @@ -3298,6 +3299,95 @@ "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", "license": "MIT" }, + "node_modules/pg": { + "version": "8.14.1", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.14.1.tgz", + "integrity": "sha512-0TdbqfjwIun9Fm/r89oB7RFQ0bLgduAhiIqIXOsyKoiC/L54DbuAAzIEN/9Op0f1Po9X7iCPXGoa/Ah+2aI8Xw==", + "license": "MIT", + "dependencies": { + "pg-connection-string": "^2.7.0", + "pg-pool": "^3.8.0", + "pg-protocol": "^1.8.0", + "pg-types": "^2.1.0", + "pgpass": "1.x" + }, + "engines": { + "node": ">= 8.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.1.1" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-cloudflare": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz", + "integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==", + "license": "MIT", + "optional": true + }, + "node_modules/pg-connection-string": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.7.0.tgz", + "integrity": "sha512-PI2W9mv53rXJQEOb8xNR8lH7Hr+EKa6oJa38zsK0S/ky2er16ios1wLKhZyxzD7jUReiWokc9WK5nxSnC7W1TA==", + "license": "MIT" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "license": "ISC", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-pool": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.8.0.tgz", + "integrity": "sha512-VBw3jiVm6ZOdLBTIcXLNdSotb6Iy3uOCwDGFAksZCXmi10nyRvnP2v3jl4d+IsLYRyXf6o9hIm/ZtUzlByNUdw==", + "license": "MIT", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.8.0.tgz", + "integrity": "sha512-jvuYlEkL03NRvOoyoRktBK7+qU5kOvlAwvmrH8sr3wbLrOdVWsRxQfz8mMy9sZFsqJ1hEWNfdWKI4SAmoL+j7g==", + "license": "MIT" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "license": "MIT", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "license": "MIT", + "dependencies": { + "split2": "^4.1.0" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -3326,6 +3416,45 @@ "node": ">=0.10.0" } }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "license": "MIT", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/pretty-bytes": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", @@ -3852,6 +3981,15 @@ "node": ">=8" } }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, "node_modules/sshpk": { "version": "1.18.0", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", @@ -4328,6 +4466,15 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "license": "ISC" }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", diff --git a/server/package.json b/server/package.json index a7ce50b..3ad0c0e 100644 --- a/server/package.json +++ b/server/package.json @@ -17,6 +17,7 @@ "cors": "^2.8.5", "cypress": "^14.2.1", "express": "^4.18.2", + "pg": "^8.14.1", "uuid": "^9.0.0" }, "devDependencies": {