mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-05-06 16:36:45 -03:00
fix(recipe): hydrate stale modal data from recipe json
This commit is contained in:
@@ -5,6 +5,9 @@ const loadingManagerMock = vi.hoisted(() => ({
|
||||
showSimpleLoading: vi.fn(),
|
||||
hide: vi.fn(),
|
||||
}));
|
||||
const virtualScrollerMock = vi.hoisted(() => ({
|
||||
updateSingleItem: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('../../../static/js/utils/uiHelpers.js', () => {
|
||||
return {
|
||||
@@ -20,12 +23,13 @@ vi.mock('../../../static/js/state/index.js', () => {
|
||||
return {
|
||||
state: {
|
||||
loadingManager: loadingManagerMock,
|
||||
virtualScroller: virtualScrollerMock,
|
||||
},
|
||||
getCurrentPageState: vi.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
import { RecipeSidebarApiClient } from '../../../static/js/api/recipeApi.js';
|
||||
import { RecipeSidebarApiClient, fetchRecipeDetails, updateRecipeMetadata } from '../../../static/js/api/recipeApi.js';
|
||||
|
||||
describe('RecipeSidebarApiClient bulk operations', () => {
|
||||
beforeEach(() => {
|
||||
@@ -111,4 +115,37 @@ describe('RecipeSidebarApiClient bulk operations', () => {
|
||||
});
|
||||
expect(loadingManagerMock.hide).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('encodes recipe IDs when fetching recipe details', async () => {
|
||||
global.fetch.mockResolvedValue({
|
||||
ok: true,
|
||||
json: async () => ({ id: 'abc' }),
|
||||
});
|
||||
|
||||
await fetchRecipeDetails('recipe#1?name=foo%bar');
|
||||
|
||||
expect(global.fetch).toHaveBeenCalledWith('/api/lm/recipe/recipe%231%3Fname%3Dfoo%25bar');
|
||||
});
|
||||
|
||||
it('updates the virtual scroller using the original list path when provided', async () => {
|
||||
global.fetch.mockResolvedValue({
|
||||
ok: true,
|
||||
json: async () => ({ success: true }),
|
||||
});
|
||||
|
||||
await updateRecipeMetadata(
|
||||
'/recipes/new-folder/recipe#1.webp',
|
||||
{ title: 'Updated Title' },
|
||||
{ listFilePath: '/recipes/old-folder/recipe#1.webp' }
|
||||
);
|
||||
|
||||
expect(global.fetch).toHaveBeenCalledWith(
|
||||
'/api/lm/recipe/recipe%231/update',
|
||||
expect.objectContaining({ method: 'PUT' })
|
||||
);
|
||||
expect(virtualScrollerMock.updateSingleItem).toHaveBeenCalledWith(
|
||||
'/recipes/old-folder/recipe#1.webp',
|
||||
{ title: 'Updated Title' }
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -358,6 +358,133 @@ async def test_get_recipe_by_id_handles_non_dict_checkpoint(recipe_scanner):
|
||||
assert recipe["checkpoint"]["file_name"] == "by-id"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_recipe_by_id_merges_recipe_json_details(recipe_scanner):
|
||||
scanner, _ = recipe_scanner
|
||||
recipes_dir = Path(scanner.recipes_dir)
|
||||
recipe_id = "hydrate-me"
|
||||
recipe_json_path = recipes_dir / f"{recipe_id}.recipe.json"
|
||||
recipe_json_path.write_text(
|
||||
json.dumps(
|
||||
{
|
||||
"id": recipe_id,
|
||||
"file_path": "/tmp/hydrate-me.png",
|
||||
"title": "Hydrated Recipe",
|
||||
"source_path": "https://example.com/source",
|
||||
"gen_params": {
|
||||
"prompt": "prompt from json",
|
||||
"negative_prompt": "negative from json",
|
||||
},
|
||||
"loras": [],
|
||||
}
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
scanner._cache.raw_data = [
|
||||
{
|
||||
"id": recipe_id,
|
||||
"file_path": "/tmp/hydrate-me.png",
|
||||
"title": "Cached Recipe",
|
||||
"folder": "",
|
||||
"modified": 0.0,
|
||||
"created_date": 0.0,
|
||||
"loras": [],
|
||||
"gen_params": {},
|
||||
}
|
||||
]
|
||||
|
||||
recipe = await scanner.get_recipe_by_id(recipe_id)
|
||||
|
||||
assert recipe is not None
|
||||
assert recipe["title"] == "Hydrated Recipe"
|
||||
assert recipe["source_path"] == "https://example.com/source"
|
||||
assert recipe["gen_params"]["prompt"] == "prompt from json"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_recipe_by_id_prefers_json_file_path(recipe_scanner):
|
||||
scanner, _ = recipe_scanner
|
||||
recipes_dir = Path(scanner.recipes_dir)
|
||||
recipe_id = "move-me"
|
||||
recipe_json_path = recipes_dir / f"{recipe_id}.recipe.json"
|
||||
recipe_json_path.write_text(
|
||||
json.dumps(
|
||||
{
|
||||
"id": recipe_id,
|
||||
"file_path": "/tmp/new-location.png",
|
||||
"title": "Moved Recipe",
|
||||
"source_path": "https://example.com/moved",
|
||||
"gen_params": {},
|
||||
"loras": [],
|
||||
}
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
scanner._cache.raw_data = [
|
||||
{
|
||||
"id": recipe_id,
|
||||
"file_path": "/tmp/old-location.png",
|
||||
"title": "Cached Title",
|
||||
"folder": "",
|
||||
"modified": 0.0,
|
||||
"created_date": 0.0,
|
||||
"loras": [],
|
||||
"gen_params": {},
|
||||
}
|
||||
]
|
||||
|
||||
recipe = await scanner.get_recipe_by_id(recipe_id)
|
||||
|
||||
assert recipe is not None
|
||||
assert recipe["file_path"] == "/tmp/new-location.png"
|
||||
assert recipe["title"] == "Moved Recipe"
|
||||
assert recipe["source_path"] == "https://example.com/moved"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_recipe_by_id_drops_deleted_optional_json_fields(recipe_scanner):
|
||||
scanner, _ = recipe_scanner
|
||||
recipes_dir = Path(scanner.recipes_dir)
|
||||
recipe_id = "drop-optional-fields"
|
||||
recipe_json_path = recipes_dir / f"{recipe_id}.recipe.json"
|
||||
recipe_json_path.write_text(
|
||||
json.dumps(
|
||||
{
|
||||
"id": recipe_id,
|
||||
"file_path": "/tmp/drop-optional-fields.png",
|
||||
"title": "Trimmed Recipe",
|
||||
}
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
scanner._cache.raw_data = [
|
||||
{
|
||||
"id": recipe_id,
|
||||
"file_path": "/tmp/drop-optional-fields.png",
|
||||
"title": "Cached Recipe",
|
||||
"folder": "",
|
||||
"modified": 0.0,
|
||||
"created_date": 0.0,
|
||||
"source_path": "https://example.com/stale-source",
|
||||
"checkpoint": {"name": "stale-checkpoint.safetensors"},
|
||||
"loras": [{"modelName": "stale-lora"}],
|
||||
"gen_params": {"prompt": "stale prompt"},
|
||||
}
|
||||
]
|
||||
|
||||
recipe = await scanner.get_recipe_by_id(recipe_id)
|
||||
|
||||
assert recipe is not None
|
||||
assert recipe["title"] == "Trimmed Recipe"
|
||||
assert "source_path" not in recipe
|
||||
assert "checkpoint" not in recipe
|
||||
assert "gen_params" not in recipe
|
||||
assert "loras" not in recipe
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_paginated_data_filters_by_checkpoint_hash(recipe_scanner):
|
||||
scanner, _ = recipe_scanner
|
||||
|
||||
Reference in New Issue
Block a user