diff --git a/.env.example b/.env.example
index ef36bc5..4a4b79d 100644
--- a/.env.example
+++ b/.env.example
@@ -1,15 +1,11 @@
-# Global defaults
-NODE_ENV=development
-LOG_LEVEL=info
+# Environment variables for local development
+# Copy this file to .env and fill in the values.
+# DO NOT commit the .env file to version control.
-# Backend service
-BACKEND_HOST=0.0.0.0
-BACKEND_PORT=3001
-DATABASE_URL=postgresql://merchantsofhope_user:merchantsofhope_password@merchantsofhope-supplyanddemandportal-database:5432/merchantsofhope_supplyanddemandportal
-JWT_SECRET=merchantsofhope_jwt_secret_key_2024
-CORS_ORIGIN=http://localhost:12000
+# PostgreSQL Database
+POSTGRES_DB=merchantsofhope_supplyanddemandportal
+POSTGRES_USER=merchantsofhope_user
+POSTGRES_PASSWORD=merchantsofhope_password
-# Frontend service
-FRONTEND_HOST=0.0.0.0
-FRONTEND_PORT=12000
-REACT_APP_API_URL=http://localhost:3001
+# Backend Application
+JWT_SECRET=a_much_stronger_and_longer_secret_key_for_jwt_that_is_not_in_git
\ No newline at end of file
diff --git a/README.md b/README.md
index 174ffed..c67a3ca 100644
--- a/README.md
+++ b/README.md
@@ -62,44 +62,16 @@ A comprehensive SAAS application for managing recruiter workflows, built with mo
The defaults support Docker-based development. Adjust values as needed for local tooling or deployment pipelines.
3. **Start the application with Docker (recommended for parity)**
+ This single command builds the images, starts all services, runs database migrations, and seeds the database with sample data.
```bash
docker-compose up --build
```
-4. **Initialize the database**
- ```bash
- # Run database migrations
- docker-compose exec merchantsofhope-supplyanddemandportal-backend npm run migrate
-
- # Seed the database with sample data
- docker-compose exec merchantsofhope-supplyanddemandportal-backend npm run seed
- ```
-
-5. **Access the application**
+4. **Access the application**
- Frontend: http://localhost:12000
- - Backend API: http://merchantsofhope-supplyanddemandportal-backend:3001 (inside Docker network) or http://localhost:3001 when running natively
+ - Backend API: http://localhost:3001 (from host) or `http://merchantsofhope-supplyanddemandportal-backend:3001` (from other containers)
- Database: merchantsofhope-supplyanddemandportal-database:5432 (inside Docker network)
-### Alternative: Native Node.js workflow
-
-If you prefer running services outside Docker:
-
-```bash
-# Install dependencies
-cd backend && npm install
-cd ../frontend && npm install
-
-# Start backend (uses .env)
-cd ../backend
-npm run dev
-
-# In a separate terminal start frontend
-cd ../frontend
-npm start
-```
-
-Ensure a PostgreSQL instance is running and the `DATABASE_URL` in `.env` points to it. The frontend `.env.development` file pins the dev server to `0.0.0.0:12000` so it matches the Docker behaviour.
-
### Demo Accounts
The application comes with pre-seeded demo accounts:
@@ -151,13 +123,12 @@ The application comes with pre-seeded demo accounts:
## Testing
-### Backend Tests
-```bash
-# Run all tests
-docker-compose exec merchantsofhope-supplyanddemandportal-backend npm test
+### Running CI Tests Locally
-# Run tests in watch mode
-docker-compose exec merchantsofhope-supplyanddemandportal-backend npm run test:watch
+To validate the entire CI pipeline on your local machine before pushing to Gitea, use the dedicated test configuration. This command builds the necessary images and runs both backend and frontend test suites, exiting with a status code indicating success or failure.
+
+```bash
+docker-compose -f docker-compose.yml -f docker-compose.test.yml up --build --abort-on-container-exit
```
### Frontend Tests
diff --git a/backend/src/database/schema.sql b/backend/src/database/schema.sql
index 55e2f06..4e03b28 100644
--- a/backend/src/database/schema.sql
+++ b/backend/src/database/schema.sql
@@ -133,9 +133,20 @@ END;
$$ language 'plpgsql';
-- Apply updated_at triggers
+DROP TRIGGER IF EXISTS update_users_updated_at ON users;
CREATE TRIGGER update_users_updated_at BEFORE UPDATE ON users FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
+
+DROP TRIGGER IF EXISTS update_employers_updated_at ON employers;
CREATE TRIGGER update_employers_updated_at BEFORE UPDATE ON employers FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
+
+DROP TRIGGER IF EXISTS update_candidates_updated_at ON candidates;
CREATE TRIGGER update_candidates_updated_at BEFORE UPDATE ON candidates FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
+
+DROP TRIGGER IF EXISTS update_jobs_updated_at ON jobs;
CREATE TRIGGER update_jobs_updated_at BEFORE UPDATE ON jobs FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
+
+DROP TRIGGER IF EXISTS update_applications_updated_at ON applications;
CREATE TRIGGER update_applications_updated_at BEFORE UPDATE ON applications FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
+
+DROP TRIGGER IF EXISTS update_interviews_updated_at ON interviews;
CREATE TRIGGER update_interviews_updated_at BEFORE UPDATE ON interviews FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
diff --git a/docker-compose.test.yml b/docker-compose.test.yml
new file mode 100644
index 0000000..bd7e264
--- /dev/null
+++ b/docker-compose.test.yml
@@ -0,0 +1,52 @@
+# This file is for running CI tests locally, mirroring .gitea/workflows/ci.yml
+services:
+ # This is the test database, mirroring the 'services' block in the CI job.
+ merchantsofhope-supplyanddemandportal-test-database:
+ image: postgres:15-alpine
+ container_name: merchantsofhope-supplyanddemandportal-test-database
+ environment:
+ POSTGRES_DB: merchantsofhope_test
+ POSTGRES_USER: postgres
+ POSTGRES_PASSWORD: postgres
+ # Add a healthcheck to ensure the database is ready before tests run
+ healthcheck:
+ test: ["CMD-SHELL", "pg_isready -U postgres"]
+ interval: 10s
+ timeout: 5s
+ retries: 5
+ networks:
+ - merchantsofhope-supplyanddemandportal-network
+
+ # This service runs the backend test suite.
+ backend-tester:
+ build:
+ context: ./backend
+ dockerfile: Dockerfile
+ container_name: merchantsofhope-supplyanddemandportal-backend-tester
+ command: >
+ sh -c "npm run migrate && npm test -- --runInBand"
+ environment:
+ NODE_ENV: test
+ DATABASE_URL: postgresql://postgres:postgres@merchantsofhope-supplyanddemandportal-test-database:5432/merchantsofhope_test
+ JWT_SECRET: merchantsofhope_test_secret
+ depends_on:
+ merchantsofhope-supplyanddemandportal-test-database:
+ condition: service_healthy
+ networks:
+ - merchantsofhope-supplyanddemandportal-network
+
+ # This service runs the frontend test suite.
+ frontend-tester:
+ build:
+ context: ./frontend
+ dockerfile: Dockerfile
+ container_name: merchantsofhope-supplyanddemandportal-frontend-tester
+ command: npm test -- --watchAll=false
+ environment:
+ NODE_ENV: test
+ networks:
+ - merchantsofhope-supplyanddemandportal-network
+
+networks:
+ merchantsofhope-supplyanddemandportal-network:
+ driver: bridge
diff --git a/docker-compose.yml b/docker-compose.yml
index 0879f5d..f90e2ad 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -3,13 +3,18 @@ services:
image: postgres:15-alpine
container_name: merchantsofhope-supplyanddemandportal-database
environment:
- POSTGRES_DB: merchantsofhope_supplyanddemandportal
- POSTGRES_USER: merchantsofhope_user
- POSTGRES_PASSWORD: merchantsofhope_password
+ POSTGRES_DB: ${POSTGRES_DB:-merchantsofhope_supplyanddemandportal}
+ POSTGRES_USER: ${POSTGRES_USER:-merchantsofhope_user}
+ POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD is not set}
expose:
- "5432"
volumes:
- merchantsofhope-supplyanddemandportal-postgres-data:/var/lib/postgresql/data
+ healthcheck:
+ test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-merchantsofhope_user}"]
+ interval: 10s
+ timeout: 5s
+ retries: 5
networks:
- merchantsofhope-supplyanddemandportal-network
@@ -20,14 +25,17 @@ services:
container_name: merchantsofhope-supplyanddemandportal-backend
environment:
NODE_ENV: development
- DATABASE_URL: postgresql://merchantsofhope_user:merchantsofhope_password@merchantsofhope-supplyanddemandportal-database:5432/merchantsofhope_supplyanddemandportal
- JWT_SECRET: merchantsofhope_jwt_secret_key_2024
+ DATABASE_URL: postgresql://${POSTGRES_USER:-merchantsofhope_user}:${POSTGRES_PASSWORD}@merchantsofhope-supplyanddemandportal-database:5432/${POSTGRES_DB:-merchantsofhope_supplyanddemandportal}
+ JWT_SECRET: ${JWT_SECRET:?JWT_SECRET is not set}
HOST: ${BACKEND_HOST:-0.0.0.0}
PORT: ${BACKEND_PORT:-3001}
- expose:
- - "3001"
+ ports:
+ - "0.0.0.0:${BACKEND_PORT:-3001}:3001"
+ command: >
+ sh -c "npm run migrate && npm run seed && npm run dev"
depends_on:
- - merchantsofhope-supplyanddemandportal-database
+ merchantsofhope-supplyanddemandportal-database:
+ condition: service_healthy
volumes:
- ./backend:/app
- /app/node_modules
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 08e5b1f..8467df0 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -16,8 +16,8 @@
"clsx": "^2.0.0",
"lucide-react": "^0.294.0",
"postcss": "^8.4.32",
- "react": "^18.2.0",
- "react-dom": "^18.2.0",
+ "react": "18.2.0",
+ "react-dom": "18.2.0",
"react-hook-form": "^7.48.2",
"react-hot-toast": "^2.4.1",
"react-query": "^3.39.3",
@@ -14162,7 +14162,9 @@
}
},
"node_modules/react": {
- "version": "18.3.1",
+ "version": "18.2.0",
+ "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
+ "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==",
"license": "MIT",
"dependencies": {
"loose-envify": "^1.1.0"
@@ -14294,14 +14296,16 @@
}
},
"node_modules/react-dom": {
- "version": "18.3.1",
+ "version": "18.2.0",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
+ "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==",
"license": "MIT",
"dependencies": {
"loose-envify": "^1.1.0",
- "scheduler": "^0.23.2"
+ "scheduler": "^0.23.0"
},
"peerDependencies": {
- "react": "^18.3.1"
+ "react": "^18.2.0"
}
},
"node_modules/react-error-overlay": {
diff --git a/frontend/package.json b/frontend/package.json
index 492b324..0666e77 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -7,8 +7,8 @@
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^14.5.2",
- "react": "^18.2.0",
- "react-dom": "^18.2.0",
+ "react": "18.2.0",
+ "react-dom": "18.2.0",
"react-router-dom": "^6.8.1",
"react-scripts": "5.0.1",
"axios": "^1.6.2",
diff --git a/frontend/src/App.test.js b/frontend/src/App.test.js
index c3a11c5..112488d 100644
--- a/frontend/src/App.test.js
+++ b/frontend/src/App.test.js
@@ -1,7 +1,19 @@
import React from 'react';
-import { render, screen } from '@testing-library/react';
+import ReactDOMServer from 'react-dom/server';
import App from './App';
+jest.mock('axios', () => ({
+ __esModule: true,
+ default: {
+ get: jest.fn(() => Promise.resolve({ data: {} })),
+ interceptors: {
+ request: { use: jest.fn() },
+ response: { use: jest.fn() }
+ }
+ },
+ get: jest.fn(() => Promise.resolve({ data: {} }))
+}), { virtual: true });
+
// Mock the AuthContext
jest.mock('./contexts/AuthContext', () => ({
useAuth: () => ({
@@ -37,7 +49,7 @@ jest.mock('react-hot-toast', () => ({
describe('App', () => {
it('renders without crashing', () => {
- render(