Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
daaacfde5e | ||
| 7ac8fe1d29 | |||
| a2462dfb6b | |||
|
|
a911224fb4 | ||
|
|
bf4bcef890 | ||
| ac6cd2e0a1 | |||
| eea03880c1 | |||
|
|
7fc263691f | ||
| c0912d36d5 | |||
| 612c2b5943 |
@@ -93,8 +93,9 @@ jobs:
|
||||
exit 1
|
||||
fi
|
||||
GITEA_SERVER_URL="https://gitea.projectium.com"
|
||||
COMMIT_MESSAGE=$(git log -1 --pretty=%s)
|
||||
VITE_APP_VERSION="$(date +'%Y%m%d-%H%M'):$(git rev-parse --short HEAD)" \
|
||||
COMMIT_MESSAGE=$(git log -1 --grep="\[skip ci\]" --invert-grep --pretty=%s)
|
||||
PACKAGE_VERSION=$(node -p "require('./package.json').version")
|
||||
VITE_APP_VERSION="$(date +'%Y%m%d-%H%M'):$(git rev-parse --short HEAD):$PACKAGE_VERSION" \
|
||||
VITE_APP_COMMIT_URL="$GITEA_SERVER_URL/${{ gitea.repository }}/commit/${{ gitea.sha }}" \
|
||||
VITE_APP_COMMIT_MESSAGE="$COMMIT_MESSAGE" \
|
||||
VITE_API_BASE_URL=/api VITE_API_KEY=${{ secrets.VITE_GOOGLE_GENAI_API_KEY }} npm run build
|
||||
@@ -162,7 +163,12 @@ jobs:
|
||||
echo "Updating schema hash in production database..."
|
||||
CURRENT_HASH=$(cat sql/master_schema_rollup.sql | dos2unix | sha256sum | awk '{ print $1 }')
|
||||
PGPASSWORD="$DB_PASSWORD" psql -v ON_ERROR_STOP=1 -h "$DB_HOST" -p 5432 -U "$DB_USER" -d "$DB_NAME" -c \
|
||||
"INSERT INTO public.schema_info (environment, schema_hash, deployed_at) VALUES ('production', '$CURRENT_HASH', NOW())
|
||||
"CREATE TABLE IF NOT EXISTS public.schema_info (
|
||||
environment VARCHAR(50) PRIMARY KEY,
|
||||
schema_hash VARCHAR(64) NOT NULL,
|
||||
deployed_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
INSERT INTO public.schema_info (environment, schema_hash, deployed_at) VALUES ('production', '$CURRENT_HASH', NOW())
|
||||
ON CONFLICT (environment) DO UPDATE SET schema_hash = EXCLUDED.schema_hash, deployed_at = NOW();"
|
||||
|
||||
UPDATED_HASH=$(PGPASSWORD="$DB_PASSWORD" psql -v ON_ERROR_STOP=1 -h "$DB_HOST" -p 5432 -U "$DB_USER" -d "$DB_NAME" -c "SELECT schema_hash FROM public.schema_info WHERE environment = 'production';" -t -A)
|
||||
|
||||
@@ -119,6 +119,11 @@ jobs:
|
||||
# --- JWT Secret for Passport authentication in tests ---
|
||||
JWT_SECRET: ${{ secrets.JWT_SECRET }}
|
||||
|
||||
# --- V8 Coverage for Server Process ---
|
||||
# This variable tells the Node.js process (our server, started by globalSetup)
|
||||
# where to output its raw V8 coverage data.
|
||||
NODE_V8_COVERAGE: '.coverage/tmp/integration-server'
|
||||
|
||||
# --- Increase Node.js memory limit to prevent heap out of memory errors ---
|
||||
# This is crucial for memory-intensive tasks like running tests and coverage.
|
||||
NODE_OPTIONS: '--max-old-space-size=8192'
|
||||
@@ -137,15 +142,15 @@ jobs:
|
||||
# The `|| true` ensures the workflow continues even if tests fail, allowing coverage to run.
|
||||
echo "--- Running Unit Tests ---"
|
||||
# npm run test:unit -- --coverage --reporter=verbose --includeTaskLocation --testTimeout=10000 --silent=passed-only || true
|
||||
npm run test:unit -- --coverage --reporter=verbose --includeTaskLocation --testTimeout=10000 --silent=passed-only --no-file-parallelism || true
|
||||
npm run test:unit -- --coverage --coverage.exclude='**/*.test.ts' --coverage.exclude='**/tests/**' --coverage.exclude='**/mocks/**' --reporter=verbose --includeTaskLocation --testTimeout=10000 --silent=passed-only --no-file-parallelism || true
|
||||
|
||||
echo "--- Running Integration Tests ---"
|
||||
npm run test:integration -- --coverage --reporter=verbose --includeTaskLocation --testTimeout=10000 --silent=passed-only || true
|
||||
npm run test:integration -- --coverage --coverage.exclude='**/*.test.ts' --coverage.exclude='**/tests/**' --coverage.exclude='**/mocks/**' --reporter=verbose --includeTaskLocation --testTimeout=10000 --silent=passed-only || true
|
||||
|
||||
echo "--- Running E2E Tests ---"
|
||||
# Run E2E tests using the dedicated E2E config which inherits from integration config.
|
||||
# We still pass --coverage to enable it, but directory and timeout are now in the config.
|
||||
npx vitest run --config vitest.config.e2e.ts --coverage --reporter=verbose --no-file-parallelism || true
|
||||
npx vitest run --config vitest.config.e2e.ts --coverage --coverage.exclude='**/*.test.ts' --coverage.exclude='**/tests/**' --coverage.exclude='**/mocks/**' --reporter=verbose --no-file-parallelism || true
|
||||
|
||||
# Re-enable secret masking for subsequent steps.
|
||||
echo "::secret-masking::"
|
||||
@@ -174,7 +179,7 @@ jobs:
|
||||
# Run c8: read raw files from the temp dir, and output an Istanbul JSON report.
|
||||
# We only generate the 'json' report here because it's all nyc needs for merging.
|
||||
echo "Server coverage report about to be generated..."
|
||||
npx c8 report --reporter=json --temp-directory .coverage/tmp/integration-server --reports-dir .coverage/integration-server
|
||||
npx c8 report --exclude='**/*.test.ts' --exclude='**/tests/**' --exclude='**/mocks/**' --reporter=json --temp-directory .coverage/tmp/integration-server --reports-dir .coverage/integration-server
|
||||
echo "Server coverage report generated. Verifying existence:"
|
||||
ls -l .coverage/integration-server/coverage-final.json
|
||||
|
||||
@@ -213,7 +218,10 @@ jobs:
|
||||
--reporter=text \
|
||||
--reporter=html \
|
||||
--report-dir .coverage/ \
|
||||
--temp-dir "$NYC_SOURCE_DIR"
|
||||
--temp-dir "$NYC_SOURCE_DIR" \
|
||||
--exclude "**/*.test.ts" \
|
||||
--exclude "**/tests/**" \
|
||||
--exclude "**/mocks/**"
|
||||
|
||||
# Re-enable secret masking for subsequent steps.
|
||||
echo "::secret-masking::"
|
||||
@@ -274,6 +282,9 @@ jobs:
|
||||
if [ -z "$DEPLOYED_HASH" ]; then
|
||||
echo "WARNING: No schema hash found in the test database."
|
||||
echo "This is expected for a first-time deployment. The hash will be set after a successful deployment."
|
||||
echo "--- Debug: Dumping schema_info table ---"
|
||||
PGPASSWORD="$DB_PASSWORD" psql -v ON_ERROR_STOP=0 -h "$DB_HOST" -p 5432 -U "$DB_USER" -d "$DB_NAME" -c "SELECT * FROM public.schema_info;" || true
|
||||
echo "----------------------------------------"
|
||||
# We allow the deployment to continue, but a manual schema update is required.
|
||||
# You could choose to fail here by adding `exit 1`.
|
||||
elif [ "$CURRENT_HASH" != "$DEPLOYED_HASH" ]; then
|
||||
@@ -297,8 +308,9 @@ jobs:
|
||||
fi
|
||||
|
||||
GITEA_SERVER_URL="https://gitea.projectium.com" # Your Gitea instance URL
|
||||
COMMIT_MESSAGE=$(git log -1 --pretty=%s)
|
||||
VITE_APP_VERSION="$(date +'%Y%m%d-%H%M'):$(git rev-parse --short HEAD)" \
|
||||
COMMIT_MESSAGE=$(git log -1 --grep="\[skip ci\]" --invert-grep --pretty=%s)
|
||||
PACKAGE_VERSION=$(node -p "require('./package.json').version")
|
||||
VITE_APP_VERSION="$(date +'%Y%m%d-%H%M'):$(git rev-parse --short HEAD):$PACKAGE_VERSION" \
|
||||
VITE_APP_COMMIT_URL="$GITEA_SERVER_URL/${{ gitea.repository }}/commit/${{ gitea.sha }}" \
|
||||
VITE_APP_COMMIT_MESSAGE="$COMMIT_MESSAGE" \
|
||||
VITE_API_BASE_URL="https://flyer-crawler-test.projectium.com/api" VITE_API_KEY=${{ secrets.VITE_GOOGLE_GENAI_API_KEY_TEST }} npm run build
|
||||
@@ -360,7 +372,7 @@ jobs:
|
||||
|
||||
echo "Installing production dependencies and restarting test server..."
|
||||
cd /var/www/flyer-crawler-test.projectium.com
|
||||
npm install --omit=dev # Install only production dependencies
|
||||
npm install --omit=dev
|
||||
# Use `startOrReload` with the ecosystem file. This is the standard, idempotent way to deploy.
|
||||
# It will START the process if it's not running, or RELOAD it if it is.
|
||||
# We also add `&& pm2 save` to persist the process list across server reboots.
|
||||
@@ -372,7 +384,12 @@ jobs:
|
||||
echo "Updating schema hash in test database..."
|
||||
CURRENT_HASH=$(cat sql/master_schema_rollup.sql | dos2unix | sha256sum | awk '{ print $1 }')
|
||||
PGPASSWORD="$DB_PASSWORD" psql -v ON_ERROR_STOP=1 -h "$DB_HOST" -p 5432 -U "$DB_USER" -d "$DB_NAME" -c \
|
||||
"INSERT INTO public.schema_info (environment, schema_hash, deployed_at) VALUES ('test', '$CURRENT_HASH', NOW())
|
||||
"CREATE TABLE IF NOT EXISTS public.schema_info (
|
||||
environment VARCHAR(50) PRIMARY KEY,
|
||||
schema_hash VARCHAR(64) NOT NULL,
|
||||
deployed_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
INSERT INTO public.schema_info (environment, schema_hash, deployed_at) VALUES ('test', '$CURRENT_HASH', NOW())
|
||||
ON CONFLICT (environment) DO UPDATE SET schema_hash = EXCLUDED.schema_hash, deployed_at = NOW();"
|
||||
|
||||
# Verify the hash was updated
|
||||
|
||||
@@ -92,8 +92,9 @@ jobs:
|
||||
exit 1
|
||||
fi
|
||||
GITEA_SERVER_URL="https://gitea.projectium.com"
|
||||
COMMIT_MESSAGE=$(git log -1 --pretty=%s)
|
||||
VITE_APP_VERSION="$(date +'%Y%m%d-%H%M'):$(git rev-parse --short HEAD)" \
|
||||
COMMIT_MESSAGE=$(git log -1 --grep="\[skip ci\]" --invert-grep --pretty=%s)
|
||||
PACKAGE_VERSION=$(node -p "require('./package.json').version")
|
||||
VITE_APP_VERSION="$(date +'%Y%m%d-%H%M'):$(git rev-parse --short HEAD):$PACKAGE_VERSION" \
|
||||
VITE_APP_COMMIT_URL="$GITEA_SERVER_URL/${{ gitea.repository }}/commit/${{ gitea.sha }}" \
|
||||
VITE_APP_COMMIT_MESSAGE="$COMMIT_MESSAGE" \
|
||||
VITE_API_BASE_URL=/api VITE_API_KEY=${{ secrets.VITE_GOOGLE_GENAI_API_KEY }} npm run build
|
||||
|
||||
@@ -18,12 +18,70 @@ module.exports = {
|
||||
NODE_ENV: 'production', // Set the Node.js environment to production
|
||||
name: 'flyer-crawler-api',
|
||||
cwd: '/var/www/flyer-crawler.projectium.com',
|
||||
// Inherit secrets from the deployment environment
|
||||
DB_HOST: process.env.DB_HOST,
|
||||
DB_USER: process.env.DB_USER,
|
||||
DB_PASSWORD: process.env.DB_PASSWORD,
|
||||
DB_NAME: process.env.DB_NAME,
|
||||
REDIS_URL: process.env.REDIS_URL,
|
||||
REDIS_PASSWORD: process.env.REDIS_PASSWORD,
|
||||
FRONTEND_URL: process.env.FRONTEND_URL,
|
||||
JWT_SECRET: process.env.JWT_SECRET,
|
||||
GEMINI_API_KEY: process.env.GEMINI_API_KEY,
|
||||
GOOGLE_MAPS_API_KEY: process.env.GOOGLE_MAPS_API_KEY,
|
||||
SMTP_HOST: process.env.SMTP_HOST,
|
||||
SMTP_PORT: process.env.SMTP_PORT,
|
||||
SMTP_SECURE: process.env.SMTP_SECURE,
|
||||
SMTP_USER: process.env.SMTP_USER,
|
||||
SMTP_PASS: process.env.SMTP_PASS,
|
||||
SMTP_FROM_EMAIL: process.env.SMTP_FROM_EMAIL,
|
||||
},
|
||||
// Test Environment Settings
|
||||
env_test: {
|
||||
NODE_ENV: 'development', // Use 'development' for test to enable more verbose logging if needed
|
||||
NODE_ENV: 'test', // Set to 'test' to match the environment purpose and disable pino-pretty
|
||||
name: 'flyer-crawler-api-test',
|
||||
cwd: '/var/www/flyer-crawler-test.projectium.com',
|
||||
// Inherit secrets from the deployment environment
|
||||
DB_HOST: process.env.DB_HOST,
|
||||
DB_USER: process.env.DB_USER,
|
||||
DB_PASSWORD: process.env.DB_PASSWORD,
|
||||
DB_NAME: process.env.DB_NAME,
|
||||
REDIS_URL: process.env.REDIS_URL,
|
||||
REDIS_PASSWORD: process.env.REDIS_PASSWORD,
|
||||
FRONTEND_URL: process.env.FRONTEND_URL,
|
||||
JWT_SECRET: process.env.JWT_SECRET,
|
||||
GEMINI_API_KEY: process.env.GEMINI_API_KEY,
|
||||
GOOGLE_MAPS_API_KEY: process.env.GOOGLE_MAPS_API_KEY,
|
||||
SMTP_HOST: process.env.SMTP_HOST,
|
||||
SMTP_PORT: process.env.SMTP_PORT,
|
||||
SMTP_SECURE: process.env.SMTP_SECURE,
|
||||
SMTP_USER: process.env.SMTP_USER,
|
||||
SMTP_PASS: process.env.SMTP_PASS,
|
||||
SMTP_FROM_EMAIL: process.env.SMTP_FROM_EMAIL,
|
||||
},
|
||||
// Development Environment Settings
|
||||
env_development: {
|
||||
NODE_ENV: 'development',
|
||||
name: 'flyer-crawler-api-dev',
|
||||
watch: true,
|
||||
ignore_watch: ['node_modules', 'logs', '*.log', 'flyer-images', '.git'],
|
||||
// Inherit secrets from the deployment environment
|
||||
DB_HOST: process.env.DB_HOST,
|
||||
DB_USER: process.env.DB_USER,
|
||||
DB_PASSWORD: process.env.DB_PASSWORD,
|
||||
DB_NAME: process.env.DB_NAME,
|
||||
REDIS_URL: process.env.REDIS_URL,
|
||||
REDIS_PASSWORD: process.env.REDIS_PASSWORD,
|
||||
FRONTEND_URL: process.env.FRONTEND_URL,
|
||||
JWT_SECRET: process.env.JWT_SECRET,
|
||||
GEMINI_API_KEY: process.env.GEMINI_API_KEY,
|
||||
GOOGLE_MAPS_API_KEY: process.env.GOOGLE_MAPS_API_KEY,
|
||||
SMTP_HOST: process.env.SMTP_HOST,
|
||||
SMTP_PORT: process.env.SMTP_PORT,
|
||||
SMTP_SECURE: process.env.SMTP_SECURE,
|
||||
SMTP_USER: process.env.SMTP_USER,
|
||||
SMTP_PASS: process.env.SMTP_PASS,
|
||||
SMTP_FROM_EMAIL: process.env.SMTP_FROM_EMAIL,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -36,12 +94,70 @@ module.exports = {
|
||||
NODE_ENV: 'production',
|
||||
name: 'flyer-crawler-worker',
|
||||
cwd: '/var/www/flyer-crawler.projectium.com',
|
||||
// Inherit secrets from the deployment environment
|
||||
DB_HOST: process.env.DB_HOST,
|
||||
DB_USER: process.env.DB_USER,
|
||||
DB_PASSWORD: process.env.DB_PASSWORD,
|
||||
DB_NAME: process.env.DB_NAME,
|
||||
REDIS_URL: process.env.REDIS_URL,
|
||||
REDIS_PASSWORD: process.env.REDIS_PASSWORD,
|
||||
FRONTEND_URL: process.env.FRONTEND_URL,
|
||||
JWT_SECRET: process.env.JWT_SECRET,
|
||||
GEMINI_API_KEY: process.env.GEMINI_API_KEY,
|
||||
GOOGLE_MAPS_API_KEY: process.env.GOOGLE_MAPS_API_KEY,
|
||||
SMTP_HOST: process.env.SMTP_HOST,
|
||||
SMTP_PORT: process.env.SMTP_PORT,
|
||||
SMTP_SECURE: process.env.SMTP_SECURE,
|
||||
SMTP_USER: process.env.SMTP_USER,
|
||||
SMTP_PASS: process.env.SMTP_PASS,
|
||||
SMTP_FROM_EMAIL: process.env.SMTP_FROM_EMAIL,
|
||||
},
|
||||
// Test Environment Settings
|
||||
env_test: {
|
||||
NODE_ENV: 'development',
|
||||
NODE_ENV: 'test',
|
||||
name: 'flyer-crawler-worker-test',
|
||||
cwd: '/var/www/flyer-crawler-test.projectium.com',
|
||||
// Inherit secrets from the deployment environment
|
||||
DB_HOST: process.env.DB_HOST,
|
||||
DB_USER: process.env.DB_USER,
|
||||
DB_PASSWORD: process.env.DB_PASSWORD,
|
||||
DB_NAME: process.env.DB_NAME,
|
||||
REDIS_URL: process.env.REDIS_URL,
|
||||
REDIS_PASSWORD: process.env.REDIS_PASSWORD,
|
||||
FRONTEND_URL: process.env.FRONTEND_URL,
|
||||
JWT_SECRET: process.env.JWT_SECRET,
|
||||
GEMINI_API_KEY: process.env.GEMINI_API_KEY,
|
||||
GOOGLE_MAPS_API_KEY: process.env.GOOGLE_MAPS_API_KEY,
|
||||
SMTP_HOST: process.env.SMTP_HOST,
|
||||
SMTP_PORT: process.env.SMTP_PORT,
|
||||
SMTP_SECURE: process.env.SMTP_SECURE,
|
||||
SMTP_USER: process.env.SMTP_USER,
|
||||
SMTP_PASS: process.env.SMTP_PASS,
|
||||
SMTP_FROM_EMAIL: process.env.SMTP_FROM_EMAIL,
|
||||
},
|
||||
// Development Environment Settings
|
||||
env_development: {
|
||||
NODE_ENV: 'development',
|
||||
name: 'flyer-crawler-worker-dev',
|
||||
watch: true,
|
||||
ignore_watch: ['node_modules', 'logs', '*.log', 'flyer-images', '.git'],
|
||||
// Inherit secrets from the deployment environment
|
||||
DB_HOST: process.env.DB_HOST,
|
||||
DB_USER: process.env.DB_USER,
|
||||
DB_PASSWORD: process.env.DB_PASSWORD,
|
||||
DB_NAME: process.env.DB_NAME,
|
||||
REDIS_URL: process.env.REDIS_URL,
|
||||
REDIS_PASSWORD: process.env.REDIS_PASSWORD,
|
||||
FRONTEND_URL: process.env.FRONTEND_URL,
|
||||
JWT_SECRET: process.env.JWT_SECRET,
|
||||
GEMINI_API_KEY: process.env.GEMINI_API_KEY,
|
||||
GOOGLE_MAPS_API_KEY: process.env.GOOGLE_MAPS_API_KEY,
|
||||
SMTP_HOST: process.env.SMTP_HOST,
|
||||
SMTP_PORT: process.env.SMTP_PORT,
|
||||
SMTP_SECURE: process.env.SMTP_SECURE,
|
||||
SMTP_USER: process.env.SMTP_USER,
|
||||
SMTP_PASS: process.env.SMTP_PASS,
|
||||
SMTP_FROM_EMAIL: process.env.SMTP_FROM_EMAIL,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -54,12 +170,70 @@ module.exports = {
|
||||
NODE_ENV: 'production',
|
||||
name: 'flyer-crawler-analytics-worker',
|
||||
cwd: '/var/www/flyer-crawler.projectium.com',
|
||||
// Inherit secrets from the deployment environment
|
||||
DB_HOST: process.env.DB_HOST,
|
||||
DB_USER: process.env.DB_USER,
|
||||
DB_PASSWORD: process.env.DB_PASSWORD,
|
||||
DB_NAME: process.env.DB_NAME,
|
||||
REDIS_URL: process.env.REDIS_URL,
|
||||
REDIS_PASSWORD: process.env.REDIS_PASSWORD,
|
||||
FRONTEND_URL: process.env.FRONTEND_URL,
|
||||
JWT_SECRET: process.env.JWT_SECRET,
|
||||
GEMINI_API_KEY: process.env.GEMINI_API_KEY,
|
||||
GOOGLE_MAPS_API_KEY: process.env.GOOGLE_MAPS_API_KEY,
|
||||
SMTP_HOST: process.env.SMTP_HOST,
|
||||
SMTP_PORT: process.env.SMTP_PORT,
|
||||
SMTP_SECURE: process.env.SMTP_SECURE,
|
||||
SMTP_USER: process.env.SMTP_USER,
|
||||
SMTP_PASS: process.env.SMTP_PASS,
|
||||
SMTP_FROM_EMAIL: process.env.SMTP_FROM_EMAIL,
|
||||
},
|
||||
// Test Environment Settings
|
||||
env_test: {
|
||||
NODE_ENV: 'development',
|
||||
NODE_ENV: 'test',
|
||||
name: 'flyer-crawler-analytics-worker-test',
|
||||
cwd: '/var/www/flyer-crawler-test.projectium.com',
|
||||
// Inherit secrets from the deployment environment
|
||||
DB_HOST: process.env.DB_HOST,
|
||||
DB_USER: process.env.DB_USER,
|
||||
DB_PASSWORD: process.env.DB_PASSWORD,
|
||||
DB_NAME: process.env.DB_NAME,
|
||||
REDIS_URL: process.env.REDIS_URL,
|
||||
REDIS_PASSWORD: process.env.REDIS_PASSWORD,
|
||||
FRONTEND_URL: process.env.FRONTEND_URL,
|
||||
JWT_SECRET: process.env.JWT_SECRET,
|
||||
GEMINI_API_KEY: process.env.GEMINI_API_KEY,
|
||||
GOOGLE_MAPS_API_KEY: process.env.GOOGLE_MAPS_API_KEY,
|
||||
SMTP_HOST: process.env.SMTP_HOST,
|
||||
SMTP_PORT: process.env.SMTP_PORT,
|
||||
SMTP_SECURE: process.env.SMTP_SECURE,
|
||||
SMTP_USER: process.env.SMTP_USER,
|
||||
SMTP_PASS: process.env.SMTP_PASS,
|
||||
SMTP_FROM_EMAIL: process.env.SMTP_FROM_EMAIL,
|
||||
},
|
||||
// Development Environment Settings
|
||||
env_development: {
|
||||
NODE_ENV: 'development',
|
||||
name: 'flyer-crawler-analytics-worker-dev',
|
||||
watch: true,
|
||||
ignore_watch: ['node_modules', 'logs', '*.log', 'flyer-images', '.git'],
|
||||
// Inherit secrets from the deployment environment
|
||||
DB_HOST: process.env.DB_HOST,
|
||||
DB_USER: process.env.DB_USER,
|
||||
DB_PASSWORD: process.env.DB_PASSWORD,
|
||||
DB_NAME: process.env.DB_NAME,
|
||||
REDIS_URL: process.env.REDIS_URL,
|
||||
REDIS_PASSWORD: process.env.REDIS_PASSWORD,
|
||||
FRONTEND_URL: process.env.FRONTEND_URL,
|
||||
JWT_SECRET: process.env.JWT_SECRET,
|
||||
GEMINI_API_KEY: process.env.GEMINI_API_KEY,
|
||||
GOOGLE_MAPS_API_KEY: process.env.GOOGLE_MAPS_API_KEY,
|
||||
SMTP_HOST: process.env.SMTP_HOST,
|
||||
SMTP_PORT: process.env.SMTP_PORT,
|
||||
SMTP_SECURE: process.env.SMTP_SECURE,
|
||||
SMTP_USER: process.env.SMTP_USER,
|
||||
SMTP_PASS: process.env.SMTP_PASS,
|
||||
SMTP_FROM_EMAIL: process.env.SMTP_FROM_EMAIL,
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "flyer-crawler",
|
||||
"version": "0.0.28",
|
||||
"version": "0.1.1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "flyer-crawler",
|
||||
"version": "0.0.28",
|
||||
"version": "0.1.1",
|
||||
"dependencies": {
|
||||
"@bull-board/api": "^6.14.2",
|
||||
"@bull-board/express": "^6.14.2",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "flyer-crawler",
|
||||
"private": true,
|
||||
"version": "0.0.28",
|
||||
"version": "0.1.1",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "concurrently \"npm:start:dev\" \"vite\"",
|
||||
|
||||
@@ -36,7 +36,7 @@ vi.mock('pdfjs-dist', () => ({
|
||||
// Mock the new config module
|
||||
vi.mock('./config', () => ({
|
||||
default: {
|
||||
app: { version: '1.0.0', commitMessage: 'Initial commit', commitUrl: '#' },
|
||||
app: { version: '20250101-1200:abc1234:1.0.0', commitMessage: 'Initial commit', commitUrl: '#' },
|
||||
google: { mapsEmbedApiKey: 'mock-key' },
|
||||
},
|
||||
}));
|
||||
@@ -588,11 +588,11 @@ describe('App Component', () => {
|
||||
// Mock the config module for this specific test
|
||||
vi.mock('./config', () => ({
|
||||
default: {
|
||||
app: { version: '1.0.1', commitMessage: 'New feature!', commitUrl: '#' },
|
||||
app: { version: '20250101-1200:abc1234:1.0.1', commitMessage: 'New feature!', commitUrl: '#' },
|
||||
google: { mapsEmbedApiKey: 'mock-key' },
|
||||
},
|
||||
}));
|
||||
localStorageMock.setItem('lastSeenVersion', '1.0.0');
|
||||
localStorageMock.setItem('lastSeenVersion', '20250101-1200:abc1234:1.0.0');
|
||||
renderApp();
|
||||
await expect(screen.findByTestId('whats-new-modal-mock')).resolves.toBeInTheDocument();
|
||||
});
|
||||
@@ -741,7 +741,7 @@ describe('App Component', () => {
|
||||
vi.mock('./config', () => ({
|
||||
default: {
|
||||
app: {
|
||||
version: '2.0.0',
|
||||
version: '20250101-1200:abc1234:2.0.0',
|
||||
commitMessage: 'A new version!',
|
||||
commitUrl: 'http://example.com/commit/2.0.0',
|
||||
},
|
||||
@@ -752,14 +752,14 @@ describe('App Component', () => {
|
||||
|
||||
it('should display the version number and commit link', () => {
|
||||
renderApp();
|
||||
const versionLink = screen.getByText(`Version: 2.0.0`);
|
||||
const versionLink = screen.getByText(`Version: 20250101-1200:abc1234:2.0.0`);
|
||||
expect(versionLink).toBeInTheDocument();
|
||||
expect(versionLink).toHaveAttribute('href', 'http://example.com/commit/2.0.0');
|
||||
});
|
||||
|
||||
it('should open the "What\'s New" modal when the question mark icon is clicked', async () => {
|
||||
// Pre-set the localStorage to prevent the modal from opening automatically
|
||||
localStorageMock.setItem('lastSeenVersion', '2.0.0');
|
||||
localStorageMock.setItem('lastSeenVersion', '20250101-1200:abc1234:2.0.0');
|
||||
|
||||
renderApp();
|
||||
expect(screen.queryByTestId('whats-new-modal-mock')).not.toBeInTheDocument();
|
||||
|
||||
@@ -535,6 +535,27 @@ describe('AI Routes (/api/ai)', () => {
|
||||
const flyerDataArg = vi.mocked(mockedDb.createFlyerAndItems).mock.calls[0][0];
|
||||
expect(flyerDataArg.store_name).toBe('Root Store');
|
||||
});
|
||||
|
||||
it('should default item quantity to 1 if missing', async () => {
|
||||
const payloadMissingQuantity = {
|
||||
checksum: 'qty-checksum',
|
||||
originalFileName: 'flyer-qty.jpg',
|
||||
extractedData: {
|
||||
store_name: 'Qty Store',
|
||||
items: [{ name: 'Item without qty', price: 100 }],
|
||||
},
|
||||
};
|
||||
|
||||
const response = await supertest(app)
|
||||
.post('/api/ai/flyers/process')
|
||||
.field('data', JSON.stringify(payloadMissingQuantity))
|
||||
.attach('flyerImage', imagePath);
|
||||
|
||||
expect(response.status).toBe(201);
|
||||
expect(mockedDb.createFlyerAndItems).toHaveBeenCalledTimes(1);
|
||||
const itemsArg = vi.mocked(mockedDb.createFlyerAndItems).mock.calls[0][1];
|
||||
expect(itemsArg[0].quantity).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('POST /check-flyer', () => {
|
||||
|
||||
@@ -424,6 +424,7 @@ router.post(
|
||||
const itemsForDb = itemsArray.map((item: Partial<ExtractedFlyerItem>) => ({
|
||||
...item,
|
||||
master_item_id: item.master_item_id === null ? undefined : item.master_item_id,
|
||||
quantity: item.quantity ?? 1, // Default to 1 to satisfy DB constraint
|
||||
view_count: 0,
|
||||
click_count: 0,
|
||||
updated_at: new Date().toISOString(),
|
||||
|
||||
@@ -34,6 +34,9 @@ export const logger = pino({
|
||||
'*.body.password',
|
||||
'*.body.newPassword',
|
||||
'*.body.currentPassword',
|
||||
'*.body.confirmPassword',
|
||||
'*.body.refreshToken',
|
||||
'*.body.token',
|
||||
],
|
||||
censor: '[REDACTED]',
|
||||
},
|
||||
|
||||
@@ -56,15 +56,15 @@ describe('Price History API Integration Test (/api/price-history)', () => {
|
||||
|
||||
// 4. Create flyer items linking the master item to the flyers with prices
|
||||
await pool.query(
|
||||
`INSERT INTO public.flyer_items (flyer_id, master_item_id, item, price_in_cents, price_display) VALUES ($1, $2, 'Apples', 199, '$1.99')`,
|
||||
`INSERT INTO public.flyer_items (flyer_id, master_item_id, item, price_in_cents, price_display, quantity) VALUES ($1, $2, 'Apples', 199, '$1.99', '1')`,
|
||||
[flyerId1, masterItemId],
|
||||
);
|
||||
await pool.query(
|
||||
`INSERT INTO public.flyer_items (flyer_id, master_item_id, item, price_in_cents, price_display) VALUES ($1, $2, 'Apples', 249, '$2.49')`,
|
||||
`INSERT INTO public.flyer_items (flyer_id, master_item_id, item, price_in_cents, price_display, quantity) VALUES ($1, $2, 'Apples', 249, '$2.49', '1')`,
|
||||
[flyerId2, masterItemId],
|
||||
);
|
||||
await pool.query(
|
||||
`INSERT INTO public.flyer_items (flyer_id, master_item_id, item, price_in_cents, price_display) VALUES ($1, $2, 'Apples', 299, '$2.99')`,
|
||||
`INSERT INTO public.flyer_items (flyer_id, master_item_id, item, price_in_cents, price_display, quantity) VALUES ($1, $2, 'Apples', 299, '$2.99', '1')`,
|
||||
[flyerId3, masterItemId],
|
||||
);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user