Files
Azornes-Comfyui-Model-Resolver/tests/test_search_source_methods.mjs
T

258 lines
9.0 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import { searchSourceMethods } from '../web/resolver/search/search_source_methods.js';
import {
getSourceDisplayLabel,
normalizeSourceKey,
normalizeSourceList,
} from '../web/resolver/utils/source_labels.js';
const {
getSearchSourceLabel,
getSearchSourceErrorMessage,
getSearchSourceErrorTooltip,
isSearchSourceRetryable,
getSearchSourceDefinitions,
getSearchResultKeysForSources,
getHashLookupSourcesForSearchSources,
clearSearchResultsForSources,
isSearchSourceEnabled,
getSearchSourceDefinition,
getSearchSourceEnabledMap,
getEnabledSearchSources,
getSearchSourcesForSelection,
} = searchSourceMethods;
function installStorage(values = {}) {
const previous = Object.getOwnPropertyDescriptor(globalThis, 'localStorage');
const storage = {
getItem(key) {
return Object.prototype.hasOwnProperty.call(values, key) ? values[key] : null;
},
setItem() {},
removeItem() {},
};
Object.defineProperty(globalThis, 'localStorage', {
configurable: true,
value: storage,
});
return () => {
if (previous) Object.defineProperty(globalThis, 'localStorage', previous);
else delete globalThis.localStorage;
};
}
test('search source labels and definitions remain stable', () => {
const dialog = {};
assert.equal(getSearchSourceLabel.call(dialog, 'all'), 'Everything');
assert.equal(getSearchSourceLabel.call(dialog, 'lora_manager_archive'), 'LoRA Manager Archive');
assert.equal(getSearchSourceLabel.call(dialog, 'unknown'), 'unknown');
assert.deepEqual(
getSearchSourceDefinitions.call(dialog).map(({ source, storageKey }) => ({ source, storageKey })),
[
{ source: 'local', storageKey: 'ModelResolver.searchSource.localEnabled' },
{ source: 'huggingface', storageKey: 'ModelResolver.searchSource.huggingFaceEnabled' },
{ source: 'civitai', storageKey: 'ModelResolver.searchSource.civitaiEnabled' },
{ source: 'civarchive', storageKey: 'ModelResolver.searchSource.civArchiveEnabled' },
{ source: 'lora_manager_archive', storageKey: 'ModelResolver.searchSource.loraManagerArchiveEnabled' },
]
);
});
test('shared source labels preserve search and hash comparison contexts', () => {
assert.equal(getSourceDisplayLabel('lora_manager_archive', { context: 'search' }), 'LoRA Manager Archive');
assert.equal(getSourceDisplayLabel('lora_manager_archive'), 'LoRA Archive');
assert.equal(getSourceDisplayLabel('lora-archive'), 'LoRA Archive');
assert.equal(getSourceDisplayLabel('download-source'), 'Selected source');
assert.equal(getSourceDisplayLabel('unknown-source', { fallback: 'Fallback' }), 'Fallback');
});
test('source normalization preserves labels, trimmed selections, and provider errors', () => {
const dialog = { getSearchSourceLabel, getSearchSourceErrorMessage };
assert.equal(normalizeSourceKey(' Civit-AI '), 'civit_ai');
assert.equal(normalizeSourceKey(' Civit-AI ', { trim: false }), ' civit_ai ');
assert.deepEqual([...normalizeSourceList([' ', ' local ', null, 'civitai'])], ['local', 'civitai']);
assert.equal(
getSourceDisplayLabel(' Civit-AI ', { fallback: 'Fallback' }),
'Fallback'
);
assert.deepEqual(
getSearchResultKeysForSources([' ', ' local ', null]),
['popular', 'model_list']
);
assert.equal(
getSearchSourceErrorMessage.call(dialog, ' CivArchive ', 'HTTP 502'),
'HTTP 502'
);
});
test('unstructured provider failures preserve their technical message', () => {
const dialog = { getSearchSourceLabel, getSearchSourceErrorMessage };
const temporaryFailure = getSearchSourceErrorMessage.call(
dialog,
'civarchive',
'CivArchive search failed: CivArchive search request failed (network error or timeout)'
);
assert.equal(
temporaryFailure,
'CivArchive search failed: CivArchive search request failed (network error or timeout)'
);
assert.equal(
getSearchSourceErrorMessage.call(dialog, 'civarchive', 'CivArchive search failed: HTTP 502'),
'CivArchive search failed: HTTP 502'
);
assert.equal(
getSearchSourceErrorMessage.call(dialog, 'civarchive', 'CivArchive search failed: HTTP 400'),
'CivArchive search failed: HTTP 400'
);
assert.equal(
getSearchSourceErrorMessage.call(dialog, 'civitai', 'CivAI request timed out'),
'CivAI request timed out'
);
assert.equal(
getSearchSourceErrorTooltip.call(dialog, 'civarchive', 'CivArchive search failed: HTTP 522'),
'CivArchive search failed: HTTP 522'
);
});
test('structured CivArchive status uses safe messages and HTTP details', () => {
const dialog = { getSearchSourceLabel, getSearchSourceErrorMessage, getSearchSourceErrorTooltip };
const status = {
state: 'unavailable',
code: 'provider_unavailable',
retryable: true,
http_status: 522,
message: 'CivArchive may be overloaded or temporarily unavailable. Please try again.'
};
assert.equal(
getSearchSourceErrorMessage.call(dialog, 'civarchive', 'CivArchive search failed: HTTP 522', status),
status.message
);
assert.equal(
getSearchSourceErrorTooltip.call(dialog, 'civarchive', '', status),
`${status.message} Details: HTTP 522`
);
assert.equal(
getSearchSourceErrorMessage.call(dialog, 'civarchive', '', {
state: 'rate_limited',
code: 'rate_limited',
retryable: true
}),
'CivArchive is rate limited. Please try again later.'
);
});
test('CivArchive transport failures are marked retryable without broadening other sources', () => {
assert.equal(isSearchSourceRetryable('civarchive', 'CivArchive search failed: HTTP 522'), true);
assert.equal(isSearchSourceRetryable('civarchive', 'CivArchive search failed: HTTP 400'), false);
assert.equal(isSearchSourceRetryable('civitai', 'request timed out'), false);
assert.equal(isSearchSourceRetryable('civarchive', '', { retryable: false }), false);
});
test('search source selection maps source ids to result and hash keys', () => {
assert.deepEqual(
getSearchResultKeysForSources('local'),
['popular', 'model_list']
);
assert.deepEqual(
getSearchResultKeysForSources('all'),
['popular', 'model_list', 'huggingface', 'civitai', 'civarchive', 'lora_manager_archive']
);
assert.deepEqual(
[...getHashLookupSourcesForSearchSources(['civitai', 'custom'])],
['civitai']
);
assert.deepEqual(
[...getHashLookupSourcesForSearchSources('all')],
['huggingface', 'civitai', 'civarchive']
);
});
test('clearing selected search sources preserves unrelated results and hash matches', () => {
const results = {
popular: { items: ['popular'] },
model_list: { items: ['local'] },
huggingface: { items: ['hf'] },
civitai: { items: ['civitai'] },
civarchive: { items: ['archive'] },
lora_manager_archive: { items: ['lora'] },
custom: [{ url: 'https://example.test/model' }],
local_hash_matches: [
{ hash_lookup_source: 'civitai', id: 'remove' },
{ hash_lookup_source: 'civarchive', id: 'keep' },
],
};
assert.deepEqual(clearSearchResultsForSources.call({
getSearchResultKeysForSources,
getHashLookupSourcesForSearchSources,
}, results, 'civitai'), {
popular: results.popular,
model_list: results.model_list,
huggingface: results.huggingface,
civitai: null,
civarchive: results.civarchive,
lora_manager_archive: results.lora_manager_archive,
custom: results.custom,
local_hash_matches: [{ hash_lookup_source: 'civarchive', id: 'keep' }],
});
});
test('source enablement defaults to enabled and honors persisted false flags', () => {
const restoreStorage = installStorage({
'ModelResolver.searchSource.civitaiEnabled': 'false',
});
try {
const dialog = {
getSearchSourceDefinitions,
getSearchSourceDefinition,
isSearchSourceEnabled,
};
assert.equal(isSearchSourceEnabled.call(dialog, 'civitai'), false);
assert.equal(isSearchSourceEnabled.call(dialog, 'huggingface'), true);
assert.equal(isSearchSourceEnabled.call(dialog, 'all'), true);
assert.deepEqual(getSearchSourceEnabledMap.call(dialog), {
local: true,
huggingface: true,
civitai: false,
civarchive: true,
lora_manager_archive: true,
});
} finally {
restoreStorage();
}
});
test('enabled source selection falls back to local and respects available sources', () => {
const dialog = {
getSearchSourceDefinitions,
getEnabledSearchSources,
isSearchSourceUsable: source => source !== 'civitai',
};
assert.deepEqual(getEnabledSearchSources.call(dialog), [
'local',
'huggingface',
'civarchive',
'lora_manager_archive',
]);
assert.deepEqual(getSearchSourcesForSelection.call(dialog, 'all'), [
'local',
'huggingface',
'civarchive',
'lora_manager_archive',
]);
assert.deepEqual(getSearchSourcesForSelection.call(dialog, 'civitai'), []);
const noSourcesDialog = {
getSearchSourceDefinitions,
getEnabledSearchSources,
isSearchSourceUsable: () => false,
};
assert.deepEqual(getEnabledSearchSources.call(noSourcesDialog), ['local']);
});