Files
DemonGatanjieu-Anomalous_Mo…/tests/test_listing_indices.py
T
DemonGatanjieuandClaude Opus 5.5 4f0fd38dd2 Videos in the gallery: All / Pictures / Videos, formats in Settings, fixes
Follow-up to PR #21:
- The header shows All / Pictures / Videos beside the search (only once the output folder has
  a video) instead of twelve chips; the formats move to Settings -> Gallery, stored as hidden
  so a format added later shows.
- GET /anomalous/gallery_images lists pictures only unless media= says otherwise, so the recipe
  cover picker and the MCP image search are unchanged; hide= drops formats (jpg covers .jpeg);
  has_videos tells the gallery whether to offer the switch. .avi is left out (Chrome cannot
  play it), .avif is a picture format in the filter too.
- The viewer stops the video however it closes (Esc included).
- Video cards have no View parameters, star or right-click menu, and the workbench steps
  through pictures only.
- 图库, not 画廊; the 1.59 CHANGELOG entry credits cocorang and wkdtjs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 14:27:48 +08:00

87 lines
4.6 KiB
Python

import asyncio
import json
from pathlib import Path
import sys
import tempfile
import unittest
from unittest import mock
PLUGIN = Path(__file__).resolve().parents[1]
sys.path[:0] = [str(PLUGIN), str(PLUGIN.parents[1])]
from api import materials, utils
def material(name, node_type='KSampler'):
return {'name': name, 'workflow': {'nodes': [{'id': 1, 'type': node_type, 'widgets_values': [1]}]}, 'timestamp': 1}
class ListingIndexTests(unittest.TestCase):
def setUp(self):
materials._cached_material_summary.cache_clear()
utils._invalidate_gallery_snapshot()
def test_warm_summary_avoids_workflow_reads_and_detects_file_changes(self):
with tempfile.TemporaryDirectory() as directory:
path = Path(directory, 'one.json')
path.write_text(json.dumps(material('first')))
with mock.patch.object(materials, '_read_material', wraps=materials._read_material) as read:
first = materials._list_materials(directory)
first[0]['name'] = 'mutated caller'
self.assertEqual(materials._list_materials(directory)[0]['name'], 'first')
self.assertEqual(read.call_count, 1)
path.write_text(json.dumps(material('changed name')))
self.assertEqual(materials._list_materials(directory)[0]['name'], 'changed name')
self.assertEqual(read.call_count, 2)
path.unlink()
self.assertEqual(materials._list_materials(directory), [])
def test_node_lookup_reads_only_matching_records_after_index_warmup(self):
with tempfile.TemporaryDirectory() as directory:
Path(directory, 'one.json').write_text(json.dumps(material('one', 'KSampler')))
Path(directory, 'two.json').write_text(json.dumps(material('two', 'CLIPTextEncode')))
materials._list_materials(directory)
request = type('Request', (), {'query': {'type': 'KSampler'}})()
with mock.patch.object(materials, 'get_materials_dir', return_value=directory), mock.patch.object(materials, '_read_material', wraps=materials._read_material) as read:
response = asyncio.run(materials.api_get_materials_by_node_type(request))
self.assertEqual(len(json.loads(response.text)['materials']), 1)
self.assertEqual(read.call_count, 1)
def test_gallery_pagination_reuses_snapshot_and_refresh_sees_new_output(self):
with tempfile.TemporaryDirectory() as directory:
Path(directory, 'one.png').write_bytes(b'image')
with mock.patch.object(utils, '_collect_gallery_images', wraps=utils._collect_gallery_images) as collect:
self.assertEqual(len(utils._gallery_images(directory)), 1)
Path(directory, 'two.png').write_bytes(b'image')
self.assertEqual(len(utils._gallery_images(directory)), 1)
self.assertEqual(collect.call_count, 1)
self.assertEqual(len(utils._gallery_images(directory, refresh=True)), 2)
utils._invalidate_gallery_snapshot()
utils._gallery_images(directory)
self.assertEqual(collect.call_count, 3)
def test_gallery_ttl_and_root_change_invalidate_snapshot(self):
with tempfile.TemporaryDirectory() as first, tempfile.TemporaryDirectory() as second:
with mock.patch.object(utils, '_collect_gallery_images', wraps=utils._collect_gallery_images) as collect:
utils._gallery_images(first)
utils._gallery_images(second)
utils._gallery_snapshot['expires'] = 0
utils._gallery_images(second)
self.assertEqual(collect.call_count, 3)
def test_gallery_lists_pictures_unless_asked_and_hides_formats(self):
from api import gallery_routes
with tempfile.TemporaryDirectory() as directory:
for name in ('a.png', 'b.jpeg', 'c.mp4', 'd.mkv', 'e.avi', 'f.txt'):
Path(directory, name).write_bytes(b'x')
names = lambda items: sorted(item['filename'] for item in items)
self.assertEqual(names(gallery_routes._gallery_listing(directory, True, '')), ['a.png', 'b.jpeg'])
self.assertEqual(names(gallery_routes._gallery_listing(directory, False, '', 'video')), ['c.mp4', 'd.mkv'])
self.assertEqual(names(gallery_routes._gallery_listing(directory, False, '', 'all', frozenset({'jpg', 'mkv'}))),
['a.png', 'c.mp4'])
images, has_videos = gallery_routes._gallery_page_source(directory, False, '', 'image', frozenset())
self.assertEqual((len(images), has_videos), (2, True))
if __name__ == '__main__':
unittest.main()