From b4385aa3f34962673d3504e441d24199cb8a80a7 Mon Sep 17 00:00:00 2001 From: Jamie Pine Date: Wed, 7 Jan 2026 12:47:38 -0800 Subject: [PATCH] Enhance test cleanup by adding core shutdown logic This commit adds core shutdown logic to multiple test scenarios in the `file_copy_pull_test.rs` and `volume_tracking_test.rs` files. The shutdown ensures that file descriptors are released after test completion, improving resource management and preventing potential leaks. This change enhances the reliability of the tests by ensuring a clean state after execution. --- core/tests/file_copy_pull_test.rs | 10 +++++- core/tests/volume_tracking_test.rs | 33 +++++++++++++++++++ .../components/SpacesSidebar/TagsGroup.tsx | 25 +++++++++----- .../src/components/Tags/TagSelector.tsx | 11 +++---- .../ts-client/src/hooks/useNormalizedQuery.ts | 16 +++++---- 5 files changed, 73 insertions(+), 22 deletions(-) diff --git a/core/tests/file_copy_pull_test.rs b/core/tests/file_copy_pull_test.rs index 700f9c222..7d03b83ef 100644 --- a/core/tests/file_copy_pull_test.rs +++ b/core/tests/file_copy_pull_test.rs @@ -196,6 +196,11 @@ async fn alice_pull_source_scenario() { } println!("Alice: PULL source test completed"); + + // Cleanup: shutdown core to release file descriptors + core.shutdown() + .await + .expect("Failed to shutdown Alice core"); } /// Bob's role in PULL test - initiates PULL to get files from Alice @@ -505,6 +510,9 @@ async fn bob_pull_receiver_scenario() { } println!("Bob: PULL test completed"); + + // Cleanup: shutdown core to release file descriptors + core.shutdown().await.expect("Failed to shutdown Bob core"); } /// Main test orchestrator for PULL operations @@ -566,4 +574,4 @@ async fn test_file_copy_pull() { panic!("PULL transfer test failed"); } } -} +} \ No newline at end of file diff --git a/core/tests/volume_tracking_test.rs b/core/tests/volume_tracking_test.rs index 51825f107..6bd90f74d 100644 --- a/core/tests/volume_tracking_test.rs +++ b/core/tests/volume_tracking_test.rs @@ -235,6 +235,9 @@ async fn test_volume_tracking_lifecycle() { } info!("Volume tracking lifecycle test completed successfully"); + + // Cleanup: shutdown core to release file descriptors + core.shutdown().await.expect("Failed to shutdown core"); } #[tokio::test] @@ -440,6 +443,9 @@ async fn test_volume_tracking_multiple_libraries() { ); info!("Multiple library volume tracking test completed successfully"); + + // Cleanup: shutdown core to release file descriptors + core.shutdown().await.expect("Failed to shutdown core"); } #[tokio::test] @@ -503,6 +509,9 @@ async fn test_automatic_system_volume_tracking() { } info!("Automatic system volume tracking test completed"); + + // Cleanup: shutdown core to release file descriptors + core.shutdown().await.expect("Failed to shutdown core"); } #[tokio::test] @@ -592,6 +601,9 @@ async fn test_auto_tracking_disabled() { } info!("Manual tracking control test completed"); + + // Cleanup: shutdown core to release file descriptors + core.shutdown().await.expect("Failed to shutdown core"); } #[tokio::test] @@ -683,6 +695,9 @@ async fn test_volume_state_updates() { ); info!("Volume state update test completed"); + + // Cleanup: shutdown core to release file descriptors + core.shutdown().await.expect("Failed to shutdown core"); } #[tokio::test] @@ -762,6 +777,9 @@ async fn test_volume_speed_test() { } info!("Volume speed test completed"); + + // Cleanup: shutdown core to release file descriptors + core.shutdown().await.expect("Failed to shutdown core"); } #[tokio::test] @@ -842,6 +860,9 @@ async fn test_volume_types_and_properties() { // Should have at least one system volume assert!(system_count > 0, "Should detect at least one system volume"); + + // Cleanup: shutdown core to release file descriptors + core.shutdown().await.expect("Failed to shutdown core"); } #[tokio::test] @@ -971,6 +992,9 @@ async fn test_volume_tracking_persistence() { } info!("Volume tracking persistence test completed"); + + // Cleanup: shutdown core to release file descriptors + core.shutdown().await.expect("Failed to shutdown core"); } #[tokio::test] @@ -1096,6 +1120,9 @@ async fn test_volume_tracking_edge_cases() { } info!("Volume edge cases test completed"); + + // Cleanup: shutdown core to release file descriptors + core.shutdown().await.expect("Failed to shutdown core"); } #[tokio::test] @@ -1164,6 +1191,9 @@ async fn test_volume_refresh_and_detection() { } info!("Volume refresh and detection test completed"); + + // Cleanup: shutdown core to release file descriptors + core.shutdown().await.expect("Failed to shutdown core"); } #[tokio::test] @@ -1268,4 +1298,7 @@ async fn test_volume_monitor_service() { // Don't stop the monitor as it's managed by Core info!("Volume monitor service test completed"); + + // Cleanup: shutdown core to release file descriptors + core.shutdown().await.expect("Failed to shutdown core"); } \ No newline at end of file diff --git a/packages/interface/src/components/SpacesSidebar/TagsGroup.tsx b/packages/interface/src/components/SpacesSidebar/TagsGroup.tsx index 546f96590..040be2ca5 100644 --- a/packages/interface/src/components/SpacesSidebar/TagsGroup.tsx +++ b/packages/interface/src/components/SpacesSidebar/TagsGroup.tsx @@ -1,4 +1,4 @@ -import { Tag as TagIcon, Plus } from '@phosphor-icons/react'; +import { Tag as TagIcon, Plus, CaretRight } from '@phosphor-icons/react'; import { useState } from 'react'; import { useNavigate } from 'react-router-dom'; import clsx from 'clsx'; @@ -98,25 +98,34 @@ export function TagsGroup({ const createTag = useLibraryMutation('tags.create'); // Fetch tags with real-time updates using search with empty query - const { data: tagsData, isLoading } = useNormalizedQuery({ + // Using select to normalize TagSearchResult[] to Tag[] for consistent cache structure + const { data: tags = [], isLoading } = useNormalizedQuery({ wireMethod: 'query:tags.search', input: { query: '' }, - resourceType: 'tag' + resourceType: 'tag', + select: (data: any) => data?.tags?.map((result: any) => result.tag || result).filter(Boolean) ?? [] }); - // Extract tags from search results - // Handle both TagSearchResult ({ tag, relevance, ... }) and raw Tag objects - // (resource events may inject raw Tag objects into the cache) - const tags = tagsData?.tags?.map((result: any) => result.tag || result).filter(Boolean) ?? []; - const handleCreateTag = async () => { if (!newTagName.trim()) return; try { const result = await createTag.mutateAsync({ canonical_name: newTagName.trim(), + display_name: null, + formal_name: null, + abbreviation: null, aliases: [], + namespace: null, + tag_type: null, color: `#${Math.floor(Math.random() * 16777215).toString(16).padStart(6, '0')}`, + icon: null, + description: null, + is_organizational_anchor: null, + privacy_level: null, + search_weight: null, + attributes: null, + apply_to: null }); // Navigate to the new tag diff --git a/packages/interface/src/components/Tags/TagSelector.tsx b/packages/interface/src/components/Tags/TagSelector.tsx index 8211cfefa..c58981ab5 100644 --- a/packages/interface/src/components/Tags/TagSelector.tsx +++ b/packages/interface/src/components/Tags/TagSelector.tsx @@ -36,17 +36,14 @@ export function TagSelector({ const createTag = useLibraryMutation('tags.create'); // Fetch all tags using search with empty query - const { data: tagsData } = useNormalizedQuery({ + // Using select to normalize TagSearchResult[] to Tag[] for consistent cache structure + const { data: allTags = [] } = useNormalizedQuery({ wireMethod: 'query:tags.search', input: { query: '' }, - resourceType: 'tag' + resourceType: 'tag', + select: (data: any) => data?.tags?.map((result: any) => result.tag || result).filter(Boolean) ?? [] }); - // Extract tags from search results - // Handle both TagSearchResult ({ tag, relevance, ... }) and raw Tag objects - // (resource events may inject raw Tag objects into the cache) - const allTags = tagsData?.tags?.map((result: any) => result.tag || result).filter(Boolean) ?? []; - // Check if query matches an existing tag const exactMatch = allTags.find( tag => tag.canonical_name.toLowerCase() === query.toLowerCase() diff --git a/packages/ts-client/src/hooks/useNormalizedQuery.ts b/packages/ts-client/src/hooks/useNormalizedQuery.ts index 8b638e620..669860beb 100644 --- a/packages/ts-client/src/hooks/useNormalizedQuery.ts +++ b/packages/ts-client/src/hooks/useNormalizedQuery.ts @@ -26,13 +26,14 @@ import { useEffect, useMemo, useState, useRef } from "react"; import { useQuery, useQueryClient, QueryClient } from "@tanstack/react-query"; import { useSpacedriveClient } from "./useClient"; import type { Event } from "../generated/types"; +import type { SdPath } from "../types"; import invariant from "tiny-invariant"; import * as v from "valibot"; import type { Simplify } from "type-fest"; // Types -export type UseNormalizedQueryOptions = Simplify<{ +export type UseNormalizedQueryOptions = Simplify<{ /** Wire method to call (e.g., "query:files.directory_listing") */ wireMethod: string; /** Input for the query */ @@ -42,13 +43,15 @@ export type UseNormalizedQueryOptions = Simplify<{ /** Whether query is enabled (default: true) */ enabled?: boolean; /** Optional path scope for server-side filtering */ - pathScope?: any; // SdPath type + pathScope?: SdPath; /** Whether to include descendants (recursive) or only direct children (exact) */ includeDescendants?: boolean; /** Resource ID for single-resource queries */ resourceId?: string; /** Enable debug logging for this query instance */ debug?: boolean; + /** Optional select function to transform query data */ + select?: (data: O) => TSelected; }>; // Runtime Validation Schemas (Valibot) @@ -93,8 +96,8 @@ const ResourceDeletedSchema = v.object({ /** * useNormalizedQuery - Main hook */ -export function useNormalizedQuery( - options: UseNormalizedQueryOptions, +export function useNormalizedQuery( + options: UseNormalizedQueryOptions, ) { const client = useSpacedriveClient(); const queryClient = useQueryClient(); @@ -121,7 +124,7 @@ export function useNormalizedQuery( ); // Standard TanStack Query - const query = useQuery({ + const query = useQuery({ queryKey, queryFn: async () => { invariant(libraryId, "Library ID must be set before querying"); @@ -131,6 +134,7 @@ export function useNormalizedQuery( ); }, enabled: (options.enabled ?? true) && !!libraryId, + select: options.select, }); // Refs for stable access to latest values without triggering re-subscription @@ -796,4 +800,4 @@ export function safeMerge( } return result; -} +} \ No newline at end of file