feat: Add unit tests for validation storage functionality

- Introduced comprehensive unit tests for the validation storage module, covering functions such as writeValidation, readValidation, getAllValidations, deleteValidation, and others.
- Implemented tests to ensure correct behavior for validation creation, retrieval, deletion, and freshness checks.
- Enhanced test coverage for edge cases, including handling of non-existent validations and directory structure validation.
This commit is contained in:
Kacper
2025-12-23 23:17:41 +01:00
parent 65319f93b4
commit ef8eaa0463
3 changed files with 357 additions and 59 deletions

View File

@@ -1,4 +1,4 @@
import { useState, useEffect, useCallback } from 'react';
import { useState, useEffect, useCallback, useRef } from 'react';
import { getElectronAPI } from '@/lib/electron';
import type { Project, StoredValidation } from '@/lib/electron';
@@ -8,71 +8,26 @@ import type { Project, StoredValidation } from '@/lib/electron';
*/
export function useUnviewedValidations(currentProject: Project | null) {
const [count, setCount] = useState(0);
const projectPathRef = useRef<string | null>(null);
// Load initial count
// Keep project path in ref for use in async functions
useEffect(() => {
if (!currentProject?.path) {
setCount(0);
return;
}
const loadCount = async () => {
try {
const api = getElectronAPI();
if (api.github?.getValidations) {
const result = await api.github.getValidations(currentProject.path);
if (result.success && result.validations) {
const unviewed = result.validations.filter((v: StoredValidation) => {
if (v.viewedAt) return false;
// Check if not stale (< 24 hours)
const hoursSince =
(Date.now() - new Date(v.validatedAt).getTime()) / (1000 * 60 * 60);
return hoursSince <= 24;
});
setCount(unviewed.length);
}
}
} catch (err) {
console.error('[useUnviewedValidations] Failed to load count:', err);
}
};
loadCount();
// Subscribe to validation events to update count
const api = getElectronAPI();
if (api.github?.onValidationEvent) {
const unsubscribe = api.github.onValidationEvent((event) => {
if (event.projectPath === currentProject.path) {
if (event.type === 'issue_validation_complete') {
// New validation completed - increment count
setCount((prev) => prev + 1);
} else if (event.type === 'issue_validation_viewed') {
// Validation was viewed - decrement count
setCount((prev) => Math.max(0, prev - 1));
}
}
});
return () => unsubscribe();
}
projectPathRef.current = currentProject?.path ?? null;
}, [currentProject?.path]);
// Function to decrement count when a validation is viewed
const decrementCount = useCallback(() => {
setCount((prev) => Math.max(0, prev - 1));
}, []);
// Function to refresh count (e.g., after marking as viewed)
const refreshCount = useCallback(async () => {
if (!currentProject?.path) return;
// Fetch and update count from server
const fetchUnviewedCount = useCallback(async () => {
const projectPath = projectPathRef.current;
if (!projectPath) return;
try {
const api = getElectronAPI();
if (api.github?.getValidations) {
const result = await api.github.getValidations(currentProject.path);
const result = await api.github.getValidations(projectPath);
if (result.success && result.validations) {
const unviewed = result.validations.filter((v: StoredValidation) => {
if (v.viewedAt) return false;
// Check if not stale (< 24 hours)
const hoursSince = (Date.now() - new Date(v.validatedAt).getTime()) / (1000 * 60 * 60);
return hoursSince <= 24;
});
@@ -80,9 +35,45 @@ export function useUnviewedValidations(currentProject: Project | null) {
}
}
} catch (err) {
console.error('[useUnviewedValidations] Failed to refresh count:', err);
console.error('[useUnviewedValidations] Failed to load count:', err);
}
}, [currentProject?.path]);
}, []);
// Load initial count and subscribe to events
useEffect(() => {
if (!currentProject?.path) {
setCount(0);
return;
}
// Load initial count
fetchUnviewedCount();
// Subscribe to validation events to update count
const api = getElectronAPI();
if (api.github?.onValidationEvent) {
const unsubscribe = api.github.onValidationEvent((event) => {
if (event.projectPath === currentProject.path) {
if (event.type === 'issue_validation_complete') {
// New validation completed - refresh count from server for consistency
fetchUnviewedCount();
} else if (event.type === 'issue_validation_viewed') {
// Validation was viewed - refresh count from server for consistency
fetchUnviewedCount();
}
}
});
return () => unsubscribe();
}
}, [currentProject?.path, fetchUnviewedCount]);
// Function to decrement count when a validation is viewed
const decrementCount = useCallback(() => {
setCount((prev) => Math.max(0, prev - 1));
}, []);
// Expose refreshCount as an alias to fetchUnviewedCount for external use
const refreshCount = fetchUnviewedCount;
return { count, decrementCount, refreshCount };
}

View File

@@ -223,7 +223,7 @@ export function GitHubIssuesView() {
const unsubscribe = api.github.onValidationEvent(handleValidationEvent);
return () => unsubscribe();
}, [currentProject?.path, selectedIssue, showValidationDialog, validationModel, muteDoneSound]);
}, [currentProject?.path, selectedIssue, showValidationDialog, muteDoneSound]);
// Cleanup audio element on unmount to prevent memory leaks
useEffect(() => {
@@ -803,7 +803,7 @@ function IssueRow({
return (
<div
className={cn(
'flex items-start gap-3 p-3 cursor-pointer hover:bg-accent/50 transition-colors',
'group flex items-start gap-3 p-3 cursor-pointer hover:bg-accent/50 transition-colors',
isSelected && 'bg-accent'
)}
onClick={onClick}