Files

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' }]
);
});