124 lines
4.9 KiB
JavaScript
124 lines
4.9 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { searchSourceMethods } from '../web/resolver/search/search_source_methods.js';
|
|
import { searchStateMethods } from '../web/resolver/search/search_state_methods.js';
|
|
|
|
function createDialog() {
|
|
return {
|
|
...searchSourceMethods,
|
|
...searchStateMethods,
|
|
searchResultCache: new Map(),
|
|
workflowSearchResultCaches: new Map(),
|
|
backgroundSearchJobs: new Map(),
|
|
getDefaultSearchBaseModel: () => 'auto',
|
|
getMissingModelKey: missing => missing.key || 'missing-key',
|
|
getWorkflowScopedQueueKey: () => 'active-workflow',
|
|
getSearchResultTimestamp: result => result?.searchedAt || result?.searched_at || null,
|
|
mergeLocalMatches: (existing, incoming) => [...existing, ...incoming],
|
|
cloneSearchState: state => JSON.parse(JSON.stringify(state)),
|
|
};
|
|
}
|
|
|
|
test('search state initializes a stable per-model cache entry', () => {
|
|
const dialog = createDialog();
|
|
const missing = { key: 'missing-key' };
|
|
|
|
const first = dialog.getSearchState(missing);
|
|
const second = dialog.getSearchState(missing);
|
|
|
|
assert.equal(first, second);
|
|
assert.equal(first.selectedSource, 'all');
|
|
assert.equal(first.selectedBaseModel, 'auto');
|
|
assert.deepEqual(first.results, {
|
|
popular: null,
|
|
model_list: null,
|
|
huggingface: null,
|
|
civitai: null,
|
|
civarchive: null,
|
|
lora_manager_archive: null,
|
|
custom: [],
|
|
local_hash_matches: [],
|
|
});
|
|
assert.deepEqual(first.sourceProgress, {});
|
|
assert.equal(first.activeSearchRunId, null);
|
|
});
|
|
|
|
test('background search jobs are scoped by workflow and missing-model key', () => {
|
|
const dialog = createDialog();
|
|
dialog.backgroundSearchJobs.set(
|
|
dialog.getBackgroundSearchJobKey('workflow-a', 'missing-a'),
|
|
{ runId: 'run-1', cancelledSources: new Set(['civitai']) }
|
|
);
|
|
|
|
assert.equal(dialog.hasBackgroundSearchJob('workflow-a', 'missing-a'), true);
|
|
assert.equal(dialog.hasBackgroundSearchJob('workflow-a', 'missing-a', 'run-1'), true);
|
|
assert.equal(dialog.hasBackgroundSearchJob('workflow-a', 'missing-a', 'run-2'), false);
|
|
assert.equal(dialog.isBackgroundSearchRunActive('workflow-a', 'missing-a', 'run-1'), true);
|
|
assert.equal(dialog.isSearchSourceCancelled('workflow-a', 'missing-a', 'run-1', 'civitai'), true);
|
|
assert.equal(dialog.isSearchSourceCancelled('workflow-a', 'missing-a', 'run-1', 'huggingface'), false);
|
|
});
|
|
|
|
test('workflow search cache separates non-active workflow state', () => {
|
|
const dialog = createDialog();
|
|
const missing = { key: 'missing-key' };
|
|
|
|
const state = dialog.getSearchStateForWorkflow('workflow-a', missing);
|
|
state.selectedSource = 'civitai';
|
|
dialog.persistSearchStateForWorkflow('workflow-a', missing, state);
|
|
|
|
assert.equal(dialog.getSearchStateForWorkflow('workflow-a', missing).selectedSource, 'civitai');
|
|
assert.equal(dialog.getSearchStateForWorkflow('active-workflow', missing).selectedSource, 'all');
|
|
});
|
|
|
|
test('mergeSearchResults preserves unchanged timestamps and clears only forced sources', () => {
|
|
const dialog = createDialog();
|
|
const existing = {
|
|
popular: { url: 'old-popular', searchedAt: 'old-time' },
|
|
model_list: { url: 'local-model', searchedAt: 'old-local' },
|
|
huggingface: { url: 'hf', searchedAt: 'hf-time' },
|
|
civitai: { url: 'same-civitai', searchedAt: 'original-time' },
|
|
civarchive: null,
|
|
lora_manager_archive: null,
|
|
custom: [{ url: 'custom' }],
|
|
local_hash_matches: [{ id: 'existing' }],
|
|
};
|
|
const newResults = {
|
|
searched_sources: ['local', 'civitai'],
|
|
popular: { url: 'new-popular' },
|
|
model_list: null,
|
|
civitai: { url: 'same-civitai' },
|
|
local_hash_matches: [{ id: 'new' }],
|
|
};
|
|
|
|
const merged = dialog.mergeSearchResults(existing, newResults, { searchedAt: 'new-time' });
|
|
assert.equal(merged.popular.searchedAt, 'new-time');
|
|
assert.equal(merged.civitai.searchedAt, 'original-time');
|
|
assert.equal(merged.model_list.url, 'local-model');
|
|
assert.deepEqual(merged.local_hash_matches, [{ id: 'existing' }, { id: 'new' }]);
|
|
assert.deepEqual(merged.custom, existing.custom);
|
|
|
|
const refreshed = dialog.mergeSearchResults(existing, {
|
|
searched_sources: ['local', 'civitai'],
|
|
local_hash_matches: [],
|
|
}, { forceRefresh: true });
|
|
assert.equal(refreshed.popular, null);
|
|
assert.equal(refreshed.model_list, null);
|
|
assert.equal(refreshed.civitai, null);
|
|
assert.equal(refreshed.huggingface.url, 'hf');
|
|
});
|
|
|
|
test('search result signatures and timestamps are deterministic', () => {
|
|
const dialog = createDialog();
|
|
const result = { url: 'https://example.test/model', filename: 'model.safetensors' };
|
|
|
|
assert.equal(
|
|
dialog.getSearchResultSignature([result]),
|
|
'https://example.test/model::model.safetensors::::::::'
|
|
);
|
|
assert.equal(dialog.areSearchResultsSame(result, { ...result }), true);
|
|
assert.deepEqual(
|
|
dialog.withSearchResultTimestamp([result], '2026-08-02T12:00:00Z'),
|
|
[{ ...result, searchedAt: '2026-08-02T12:00:00Z' }]
|
|
);
|
|
});
|