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.
This commit is contained in:
Jamie Pine
2026-01-07 12:47:38 -08:00
parent d1db406cda
commit b4385aa3f3
5 changed files with 73 additions and 22 deletions

View File

@@ -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");
}
}
}
}

View File

@@ -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");
}

View File

@@ -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

View File

@@ -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()

View File

@@ -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<I> = Simplify<{
export type UseNormalizedQueryOptions<I, O = any, TSelected = O> = Simplify<{
/** Wire method to call (e.g., "query:files.directory_listing") */
wireMethod: string;
/** Input for the query */
@@ -42,13 +43,15 @@ export type UseNormalizedQueryOptions<I> = 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<I, O>(
options: UseNormalizedQueryOptions<I>,
export function useNormalizedQuery<I, O = any, TSelected = O>(
options: UseNormalizedQueryOptions<I, O, TSelected>,
) {
const client = useSpacedriveClient();
const queryClient = useQueryClient();
@@ -121,7 +124,7 @@ export function useNormalizedQuery<I, O>(
);
// Standard TanStack Query
const query = useQuery<O>({
const query = useQuery<O, Error, TSelected>({
queryKey,
queryFn: async () => {
invariant(libraryId, "Library ID must be set before querying");
@@ -131,6 +134,7 @@ export function useNormalizedQuery<I, O>(
);
},
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;
}
}