Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
76398cd
KAAV-3492 validation logic change to make validation faster
henrihaapalasiili Jan 14, 2026
8e316a4
KAAV-3492 simplified showing erross at validation saga
henrihaapalasiili Jan 14, 2026
c81a841
KAAV-3492 removed non used UPDATE_PROJECT_FAILURE and added put error
henrihaapalasiili Jan 14, 2026
cc7adce
KAAV-3492 modified timeutil test to use dynamic dates
henrihaapalasiili Jan 14, 2026
f98ac1d
KAAV-3492: Fix race condition in timeline validation
henrihaapalasiili Jan 15, 2026
355195d
KAAV-3492: Improve timetable validation flow
henrihaapalasiili Jan 15, 2026
10af7f2
guard to prevent cascade loops during validation
henrihaapalasiili Jan 16, 2026
2d99527
KAAV-3492 Sync phase bar boundaries - each phase end = next phase sta…
henrihaapalasiili Jan 16, 2026
7c652de
KAAV-3492 trigger validation after adding a group, ensuring the backe…
henrihaapalasiili Jan 19, 2026
64e2431
KAAV-3517: Add phase start boundaries to confirmed_fields when first …
henrihaapalasiili Jan 20, 2026
5307c13
KAAV-3517: Trigger validation when removing deadline groups
henrihaapalasiili Jan 20, 2026
f8f053d
KAAV-3492: Fix esilläolo OFF handling and adjustDeadlineData
henrihaapalasiili Jan 20, 2026
b4f60c2
KAAV-3492: Use DB distance values instead of hardcoded fallbacks
henrihaapalasiili Jan 23, 2026
247ca81
refactor(tests): reduce duplication in timeUtil.test.js
henrihaapalasiili Jan 23, 2026
221dfb5
mock data sonarlcloud suggestion fix
henrihaapalasiili Jan 26, 2026
f419db7
fixex local time vs Github UTC time comparison on test
henrihaapalasiili Jan 26, 2026
1312133
KAAV-3492 Save original values before mutation to prevent cascading a…
henrihaapalasiili Jan 26, 2026
00e50dd
KAAV-3492 commented not needed test because logic changes
henrihaapalasiili Jan 26, 2026
15fcd98
sonarcloud false positive fix
henrihaapalasiili Jan 26, 2026
5405f50
sonarcloud false positive fix
henrihaapalasiili Jan 27, 2026
efc2625
KAAV-3492 Fixing Frontend Distance Calculation
henrihaapalasiili Jan 27, 2026
c229f10
KAAV-3492 when deleting phases the phase lengths leaves a gap between…
henrihaapalasiili Feb 2, 2026
af32752
commented annoying local redux spam
henrihaapalasiili Feb 3, 2026
31c6565
Fix cross-phase cascade pushing OAS deadlines when adding periaatteet…
henrihaapalasiili Feb 4, 2026
7d719bd
Deleting and re-adding first esillaolo crashes page fix
henrihaapalasiili Feb 9, 2026
6e6e07e
- Add getDateFieldsForDeadlineGroup() to clear dates on group deletion
henrihaapalasiili Feb 10, 2026
0640e1e
fixed test to check correct vals
henrihaapalasiili Feb 10, 2026
68421b1
Fixed the bug where moving tarkistettu_ehdotus_kylk_maaraaika backwar…
henrihaapalasiili Feb 11, 2026
8460c68
cleaned comments
henrihaapalasiili Feb 11, 2026
b7f545d
fix(timeline): filter viimeistaan_mielipiteet from cascade to prevent…
henrihaapalasiili Feb 11, 2026
f725c33
fix
henrihaapalasiili Feb 11, 2026
40c84a8
Timeline saving flag and validation objectutil logic changes to use a…
henrihaapalasiili Feb 13, 2026
436ae48
re-enabeling added phase leaves calculation wrong and not implementin…
henrihaapalasiili Feb 15, 2026
a08bd23
Use calculated values for timeline rendering to prevent visual jump
henrihaapalasiili Feb 16, 2026
39ca772
fix delete ehodut,luonnos,periaatteet phase, save, come back adding n…
henrihaapalasiili Feb 16, 2026
3b98f3f
fixes for legth when adding first lautakunta when delted
henrihaapalasiili Feb 16, 2026
1d2bac8
more robust check for re-adding first phase after saving
henrihaapalasiili Feb 17, 2026
f2f3a46
re-adding periaatteet,luonnos,esilläolo phases lenght to be correct
henrihaapalasiili Feb 17, 2026
5b3cca3
moving princibles board dates backwards next to käynnistys end not to…
henrihaapalasiili Feb 17, 2026
02fdc78
U1 date to sync with K1
henrihaapalasiili Feb 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -87,14 +87,15 @@
"lint-fix": "eslint . --fix",
"codecov": "codecov",
"precommit": "yarn test && yarn run lint",
"test-coverage": "vitest run --coverage"
"test-coverage": "vitest run --coverage",
"regenerate-mock-data": "node scripts/regenerate-mock-data.mjs"
},
"devDependencies": {
"@testing-library/dom": "^9.0.1",
"@testing-library/react": "^14.0.0",
"@testing-library/user-event": "^14.4.3",
"@vitejs/plugin-react": "^4.6.0",
"@vitest/coverage-c8": "^0.33.0",
"@vitest/coverage-v8": "^4.0.0",
"babel-eslint": "^10.1.0",
"codecov": "^3.8.3",
"deep-freeze": "0.0.1",
Expand All @@ -113,7 +114,7 @@
"sass-embedded": "^1.89.2",
"vite": "^7.1.3",
"vite-plugin-svgr": "^4.3.0",
"vitest": "^3.2.4",
"vitest": "^4.0.0",
"vitest-dom": "^0.1.1"
},
"proxy": "http://localhost:8000",
Expand Down
325 changes: 325 additions & 0 deletions scripts/regenerate-mock-data.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,325 @@
#!/usr/bin/env node
/**
* Mock Data Regeneration Script
*
* This script regenerates test mock data from the real Kaavapino API.
* It ensures tests use realistic data that matches the current database state.
*
* Usage:
* node scripts/regenerate-mock-data.mjs [--api-url URL] [--output-dir DIR]
*
* Options:
* --api-url Base URL of the Kaavapino API (default: http://localhost:8000)
* --output-dir Directory to write mock data files (default: src/__tests__/utils)
* --dry-run Print what would be generated without writing files
*
* Environment Variables:
* KAAVAPINO_API_URL Alternative way to set the API URL
* KAAVAPINO_API_TOKEN Bearer token for API authentication
*
* Prerequisites:
* - Kaavapino backend running locally or accessible via network
* - API token with read access to project schemas and date types
*/

import fs from 'fs/promises';
import path from 'path';
import { fileURLToPath } from 'url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

// Parse command line arguments
const args = process.argv.slice(2);
const getArg = (name) => {
const index = args.indexOf(name);
return index !== -1 ? args[index + 1] : null;
};

const API_URL = getArg('--api-url') || process.env.KAAVAPINO_API_URL || 'http://localhost:8000';
const OUTPUT_DIR = getArg('--output-dir') || path.join(__dirname, '..', 'src', '__tests__', 'utils');
const DRY_RUN = args.includes('--dry-run');
const API_TOKEN = process.env.KAAVAPINO_API_TOKEN || '';

console.log('🔄 Mock Data Regeneration Script');
console.log('================================');
console.log(`API URL: ${API_URL}`);
console.log(`Output Directory: ${OUTPUT_DIR}`);
console.log(`Dry Run: ${DRY_RUN}`);
console.log('');

/**
* Fetch data from the API with authentication
*/
async function fetchFromApi(endpoint) {
const headers = {
'Content-Type': 'application/json',
};

if (API_TOKEN) {
headers['Authorization'] = `Bearer ${API_TOKEN}`;
}

try {
const response = await fetch(`${API_URL}${endpoint}`, { headers });

if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}

return await response.json();
} catch (error) {
console.error(`❌ Failed to fetch ${endpoint}: ${error.message}`);
return null;
}
}

/**
* Generate date type arrays (arkipäivät, työpäivät, lautakuntapäivät)
* These are generated algorithmically to match the backend's date type logic
*/
function generateDateTypes() {
const startYear = new Date().getFullYear();
const endYear = startYear + 5;

const arkipäivät = [];
const työpäivät = [];
const lautakuntapäivät = [];
const esilläolopäivät = [];

const startDate = new Date(`${startYear}-01-01`);
const endDate = new Date(`${endYear}-12-31`);

for (let d = startDate.getTime(); d <= endDate.getTime(); d += 86400000) {
const currentDate = new Date(d);
const day = currentDate.getDay();
const month = currentDate.getMonth() + 1;
const date = currentDate.getDate();
const year = currentDate.getFullYear();
const dateStr = `${year}-${String(month).padStart(2, '0')}-${String(date).padStart(2, '0')}`;

// Arkipäivät: Weekdays (Mon-Fri)
if (day !== 0 && day !== 6) {
arkipäivät.push(dateStr);

// Työpäivät: Weekdays excluding July and Christmas period
const isJuly = month === 7;
const isChristmasPeriod = (month === 12 && date >= 24) || (month === 1 && date <= 6);

if (!isJuly && !isChristmasPeriod) {
työpäivät.push(dateStr);

// Esilläolopäivät: Työpäivät excluding weeks 8 and 42
const weekNumber = getWeekNumber(currentDate);
if (weekNumber !== 8 && weekNumber !== 42) {
esilläolopäivät.push(dateStr);
}
}
}

// Lautakuntapäivät: Tuesdays excluding July
if (day === 2 && month !== 7) {
lautakuntapäivät.push(dateStr);
}
}

return { arkipäivät, työpäivät, lautakuntapäivät, esilläolopäivät };
}

/**
* Get ISO week number for a date
*/
function getWeekNumber(date) {
const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
const dayNum = d.getUTCDay() || 7;
d.setUTCDate(d.getUTCDate() + 4 - dayNum);
const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
return Math.ceil((((d - yearStart) / 86400000) + 1) / 7);
}

/**
* Fetch deadline distances from the API
*/
async function fetchDeadlineDistances() {
console.log('📡 Fetching deadline distances from API...');

// Try to fetch from the deadline distances endpoint
const distances = await fetchFromApi('/v1/deadline-distances/');

if (distances) {
console.log(`✅ Fetched ${distances.length || 'unknown'} deadline distance rules`);
return distances;
}

console.log('⚠️ Could not fetch deadline distances, using static data');
return null;
}

/**
* Fetch project schema to get deadline field definitions
*/
async function fetchProjectSchema() {
console.log('📡 Fetching project schema from API...');

const schema = await fetchFromApi('/v1/projects/schema/');

if (schema) {
console.log(`✅ Fetched project schema`);
return schema;
}

console.log('⚠️ Could not fetch project schema');
return null;
}

/**
* Generate the checkForDecreasingValues_test_data.js file content
*/
function generateTestDataFile(dateTypes, deadlineDistances) {
const { arkipäivät, työpäivät, lautakuntapäivät, esilläolopäivät } = dateTypes;

let content = `/**
* Auto-generated mock data for checkForDecreasingValues tests
*
* Generated at: ${new Date().toISOString()}
*
* This file contains:
* - Date type arrays (arkipäivät, työpäivät, lautakuntapäivät, esilläolopäivät)
* - Sample deadline timeline array with distance rules
*
* To regenerate: node scripts/regenerate-mock-data.mjs
*/

const generateMockArkipäivät = () => {
const dates = [];
let currentDate = new Date("${arkipäivät[0]}");
const endDate = new Date("${arkipäivät[arkipäivät.length - 1]}");

while (currentDate <= endDate) {
const day = currentDate.getDay();
if (day !== 0 && day !== 6) { // Exclude Sundays (0) and Saturdays (6)
const year = currentDate.getFullYear();
const month = String(currentDate.getMonth() + 1).padStart(2, '0');
const date = String(currentDate.getDate()).padStart(2, '0');
dates.push(\`\${year}-\${month}-\${date}\`);
}
currentDate.setDate(currentDate.getDate() + 1);
}
return dates;
}

const generateMockTyöpäivät = () => {
const dates = [];
let currentDate = new Date("${työpäivät[0]}");
const endDate = new Date("${työpäivät[työpäivät.length - 1]}");

// Exclude weekends, all july dates, and dates from 24.12 to 6.1
while (currentDate <= endDate) {
const day = currentDate.getDay();
const month = String(currentDate.getMonth() + 1).padStart(2, '0');
const date = String(currentDate.getDate()).padStart(2, '0');
if (day !== 0 && day !== 6 && month !== '07' &&
!(month === '12' && date >= '24') && !(month === '01' && date <= '06')) {
const year = currentDate.getFullYear();
dates.push(\`\${year}-\${month}-\${date}\`);
}
currentDate.setDate(currentDate.getDate() + 1);
}
return dates;
}

const generateMockLautakuntapäivät = () => {
const dates = [];
let currentDate = new Date("${lautakuntapäivät[0]}");
const endDate = new Date("${lautakuntapäivät[lautakuntapäivät.length - 1]}");

while (currentDate <= endDate) {
const day = currentDate.getDay();
const month = String(currentDate.getMonth() + 1).padStart(2, '0');
if (day === 2 && month !== '07') { // Tuesdays (2) excluding July
const year = currentDate.getFullYear();
const date = String(currentDate.getDate()).padStart(2, '0');
dates.push(\`\${year}-\${month}-\${date}\`);
}
currentDate.setDate(currentDate.getDate() + 1);
}
return dates;
}

const generateMockEsillaolopaivat = () => {
const base_dates = generateMockTyöpäivät();
return base_dates.filter(date => {
const dateObj = new Date(date)
const weekNumber = Math.ceil((((dateObj - new Date(dateObj.getFullYear(),0,1)) / 86400000) + dateObj.getDay()+1)/7);
return !(weekNumber === 8 || weekNumber === 42);
});
}

`;

// If we have deadline distances from the API, include them
if (deadlineDistances && Array.isArray(deadlineDistances)) {
content += `// Deadline distances fetched from API
const deadline_distances = ${JSON.stringify(deadlineDistances, null, 2)};

`;
}

// Add the sample test array (this would be fetched from a real project in production)
content += `// Sample decreasing test array - regenerate from real project data as needed
// Use: node scripts/regenerate-mock-data.mjs --project-id <id>
`;

return content;
}

/**
* Main execution
*/
async function main() {
try {
// Generate date types
console.log('📅 Generating date type arrays...');
const dateTypes = generateDateTypes();
console.log(`✅ Generated ${dateTypes.arkipäivät.length} arkipäivät`);
console.log(`✅ Generated ${dateTypes.työpäivät.length} työpäivät`);
console.log(`✅ Generated ${dateTypes.lautakuntapäivät.length} lautakuntapäivät`);
console.log(`✅ Generated ${dateTypes.esilläolopäivät.length} esilläolopäivät`);

// Try to fetch from API
const deadlineDistances = await fetchDeadlineDistances();
const projectSchema = await fetchProjectSchema();

// Generate file content
const content = generateTestDataFile(dateTypes, deadlineDistances);

if (DRY_RUN) {
console.log('\n📝 Would generate the following content:');
console.log('---');
console.log(content.substring(0, 2000) + '...');
console.log('---');
console.log('\n✅ Dry run complete. No files written.');
} else {
// Write to file
const outputPath = path.join(OUTPUT_DIR, 'mock_date_types.generated.js');
await fs.writeFile(outputPath, content, 'utf-8');
console.log(`\n✅ Written mock data to: ${outputPath}`);
}

// Summary
console.log('\n📊 Summary:');
console.log('- Date types: Generated algorithmically');
console.log(`- Deadline distances: ${deadlineDistances ? 'Fetched from API' : 'Not available'}`);
console.log(`- Project schema: ${projectSchema ? 'Fetched from API' : 'Not available'}`);

if (!deadlineDistances || !projectSchema) {
console.log('\n💡 Tip: Start the backend server and set KAAVAPINO_API_TOKEN to fetch live data');
}

} catch (error) {
console.error('\n❌ Error:', error.message);
process.exit(1);
}
}

main();
Loading