Parallelize test running (#166)

* Allow specifying DB file explicitely.

* Export config getter/setter instead of object

Allows dynamically changing configuration

* Remove maxWorker Jest limit, parallelize tests

* Remove unnecessary slow step in Dockerfile.

* Correct error response during login

* Fix setting DB permissions in docker entrypoint

---------

Co-authored-by: antanst <>
This commit is contained in:
Antonis Anastasiadis 2025-07-16 13:27:57 +03:00 committed by GitHub
parent dad0bd45ff
commit c24bff1882
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 51 additions and 30 deletions

View file

@ -56,10 +56,10 @@ RUN apk add --no-cache --virtual .runtime-deps \
WORKDIR /app
# Copy backend
COPY ./backend/ /app/backend/
COPY --chown=app:app ./backend/ /app/backend/
RUN chmod +x /app/backend/cmd/start.sh
COPY ./scripts/docker-entrypoint.sh /app/scripts/docker-entrypoint.sh
COPY --chown=app:app ./scripts/docker-entrypoint.sh /app/scripts/docker-entrypoint.sh
RUN chmod +x /app/scripts/docker-entrypoint.sh
# Copy frontend
@ -70,8 +70,7 @@ COPY --from=builder --chown=app:app /app/node_modules ./node_modules
COPY --from=builder --chown=app:app /app/package.json /app/
# Create necessary directories
RUN mkdir -p /app/backend/db /app/backend/certs && \
chown -R app:app /app
RUN mkdir -p /app/backend/db /app/backend/certs
# Cleanup
RUN apk del --no-cache .runtime-deps sqlite openssl curl && \
@ -84,6 +83,7 @@ VOLUME ["/app/backend/db"]
EXPOSE 3002
ENV NODE_ENV=production \
DB_FILE="db/production.sqlite3" \
PORT=3002 \
TUDUDI_INTERNAL_SSL_ENABLED=false \
TUDUDI_ALLOWED_ORIGINS="http://localhost:8080,http://localhost:3002,http://127.0.0.1:8080,http://127.0.0.1:3002" \

View file

@ -10,7 +10,8 @@ const SequelizeStore = require('connect-session-sequelize')(session.Store);
const { sequelize } = require('./models');
const { initializeTelegramPolling } = require('./services/telegramInitializer');
const taskScheduler = require('./services/taskScheduler');
const config = require('./config/config');
const { setConfig, getConfig } = require('./config/config');
const config = getConfig();
const app = express();

View file

@ -1,9 +1,6 @@
#!/bin/sh
set -eu
DB_FILE="db/production.sqlite3"
[ "$NODE_ENV" = "development" ] && DB_FILE="db/development.sqlite3"
# Check if database exists and create/authenticate
if [ ! -f "$DB_FILE" ]; then
echo "Creating new database..."

View file

@ -14,9 +14,6 @@ if (
const environment = process.env.NODE_ENV;
const production = process.env.NODE_ENV === 'production';
const projectRootPath = path.join(__dirname, '..'); // backend root path
const dbDir = process.env.DATABASE_URL
? process.env.DATABASE_URL.replace('sqlite:///', '')
: path.join(projectRootPath, 'db');
const credentials = {
google: {
@ -40,9 +37,9 @@ const config = {
'http://127.0.0.1:9292',
],
dbDir,
dbFile: path.join(dbDir, `${environment}.sqlite3`),
dbFile:
process.env.DB_FILE ||
path.join(projectRootPath, 'db', `${environment}.sqlite3`),
disableScheduler: process.env.DISABLE_SCHEDULER === 'true',
@ -72,6 +69,16 @@ const config = {
production && process.env.TUDUDI_INTERNAL_SSL_ENABLED === 'true',
};
// Configuration logging removed for cleaner test output
console.log(`Using database file '${config.dbFile}'`);
module.exports = config;
function setConfig({ dbFile } = {}) {
if (dbFile != null) {
config.dbFile = dbFile;
}
}
function getConfig() {
return config;
}
module.exports = { setConfig, getConfig };

View file

@ -1,11 +1,12 @@
require('dotenv').config();
const path = require('path');
const config = require('./config');
const { setConfig, getConfig } = require('../config/config');
const config = getConfig();
module.exports = {
development: {
dialect: 'sqlite',
storage: path.join(config.dbDir, 'development.sqlite3'),
storage: config.dbFile,
logging: console.log,
define: {
timestamps: true,
@ -16,7 +17,7 @@ module.exports = {
},
test: {
dialect: 'sqlite',
storage: path.join(config.dbDir, 'test.sqlite3'),
storage: config.dbFile,
logging: false,
define: {
timestamps: true,
@ -27,7 +28,7 @@ module.exports = {
},
production: {
dialect: 'sqlite',
storage: path.join(config.dbDir, 'production.sqlite3'),
storage: config.dbFile,
logging: false,
define: {
timestamps: true,

View file

@ -2,7 +2,6 @@ module.exports = {
testEnvironment: 'node',
setupFilesAfterEnv: ['<rootDir>/tests/helpers/setup.js'],
testMatch: ['<rootDir>/tests/**/*.test.js', '<rootDir>/tests/**/*.spec.js'],
maxWorkers: 1,
collectCoverageFrom: [
'routes/**/*.js',
'models/**/*.js',

View file

@ -1,5 +1,5 @@
const { DataTypes } = require('sequelize');
const sequelize = require('../config/database');
const { sequelize } = require('./models');
const CalendarToken = sequelize.define(
'CalendarToken',

View file

@ -1,6 +1,7 @@
const { Sequelize } = require('sequelize');
const path = require('path');
const config = require('../config/config');
const { getConfig } = require('../config/config');
const config = getConfig();
// Database configuration
let dbConfig;

View file

@ -57,6 +57,13 @@ router.post('/login', async (req, res) => {
req.session.userId = user.id;
await new Promise((resolve, reject) => {
req.session.save((err) => {
if (err) reject(err);
else resolve();
});
});
res.json({
user: {
id: user.id,

View file

@ -2,7 +2,8 @@ const express = require('express');
const router = express.Router();
const { google } = require('googleapis');
const { requireAuth } = require('../middleware/auth');
const config = require('../config/config');
const { setConfig, getConfig } = require('../config/config');
const config = getConfig();
// Google Calendar configuration
const SCOPES = ['https://www.googleapis.com/auth/calendar.readonly'];

View file

@ -2,7 +2,8 @@
const path = require('path');
const { seedDatabase } = require('../seeders/dev-seeder');
const config = require('../config/config');
const { setConfig, getConfig } = require('../config/config');
const config = getConfig();
console.log('🌱 Starting development data seeding...');
console.log(`📁 Database: ${config.dbFile}`);

View file

@ -2,7 +2,8 @@ const cron = require('node-cron');
const { User } = require('../models');
const TaskSummaryService = require('./taskSummaryService');
const RecurringTaskService = require('./recurringTaskService');
const config = require('../config/config');
const { setConfig, getConfig } = require('../config/config');
const config = getConfig();
// Create scheduler state
const createSchedulerState = () => ({

View file

@ -1,6 +1,7 @@
const telegramPoller = require('./telegramPoller');
const { User } = require('../models');
const config = require('../config/config');
const { setConfig, getConfig } = require('../config/config');
const config = getConfig();
async function initializeTelegramPolling() {
if (config.environment === 'test' || config.disableTelegram) {

View file

@ -1,6 +1,9 @@
// Set test environment before importing models
process.env.NODE_ENV = 'test';
// This file is run once per each Jest worker,
// so changing the DB in the beginning of this file
// works.
const testId = require('crypto').randomBytes(4).toString('hex');
process.env.DB_FILE = `/tmp/test-${testId}.sqlite3`;
const { sequelize } = require('../../models');
beforeAll(async () => {

View file

@ -1,6 +1,6 @@
const request = require('supertest');
const app = require('../../app');
const { User } = require('../../models');
require('../../models');
const { createTestUser } = require('../helpers/testUtils');
describe('Auth Routes', () => {

View file

@ -45,6 +45,7 @@ if [ "$CURRENT_UID" != "$PUID" ] || [ "$CURRENT_GID" != "$PGID" ]; then
mkdir -p /app/backend/db /app/backend/certs
chown -R app:$TARGET_GROUP /app/backend/db /app/backend/certs
chmod 770 /app/backend/db /app/backend/certs
chmod 660 "$DB_FILE"
echo "User configuration completed"
else