198 lines
6.6 KiB
JavaScript
198 lines
6.6 KiB
JavaScript
import test from 'node:test'
|
|
import assert from 'node:assert/strict'
|
|
|
|
import {
|
|
MAX_SEARCH_HINT_ACTIONS,
|
|
MAX_SEARCH_HINT_DASHBOARDS,
|
|
MAX_SEARCH_HINT_ENTITIES_PER_TYPE,
|
|
clearSearchIndex,
|
|
dashboardRoutePath,
|
|
indexRootDashboardEntries,
|
|
indexSearchHints,
|
|
indexSystemNavigation,
|
|
searchIndexItems
|
|
} from '../stores/searchIndex.js'
|
|
|
|
function resetIndex () {
|
|
clearSearchIndex()
|
|
}
|
|
|
|
test('dashboardRoutePath maps Actions to root', () => {
|
|
assert.equal(dashboardRoutePath('Actions'), '/')
|
|
})
|
|
|
|
test('dashboardRoutePath builds dashboard and entity paths', () => {
|
|
assert.equal(dashboardRoutePath('Servers'), '/dashboards/Servers')
|
|
assert.equal(
|
|
dashboardRoutePath('Servers', 'host', 'web01'),
|
|
'/dashboards/Servers/host/web01'
|
|
)
|
|
})
|
|
|
|
test('indexSearchHints indexes entities and actions', () => {
|
|
resetIndex()
|
|
|
|
indexSearchHints({
|
|
entities: [
|
|
{ title: 'web01', type: 'host', uniqueKey: '0' },
|
|
{ title: '', type: 'host', uniqueKey: '1' },
|
|
{ title: 'skip', type: '', uniqueKey: 'x' }
|
|
],
|
|
actions: [
|
|
{ title: 'Ping Host', bindingId: 'bind-ping' },
|
|
{ title: '', bindingId: 'bind-empty-title' },
|
|
{ title: 'Ignored', bindingId: '' }
|
|
]
|
|
})
|
|
|
|
const byId = Object.fromEntries(searchIndexItems.value.map((item) => [item.id, item]))
|
|
|
|
assert.equal(byId['entity:host:0'].title, 'web01')
|
|
assert.equal(byId['entity:host:0'].path, '/entity-details/host/0')
|
|
assert.equal(byId['entity:host:0'].description, 'host')
|
|
assert.equal(byId['entity:host:1'].title, '1')
|
|
|
|
assert.equal(byId['action:bind-ping'].title, 'Ping Host')
|
|
assert.equal(byId['action:bind-ping'].path, '/action/bind-ping')
|
|
assert.equal(byId['action:bind-ping'].category, 'Actions')
|
|
assert.equal(byId['action:bind-empty-title'].title, 'bind-empty-title')
|
|
|
|
assert.equal(Object.keys(byId).includes('entity:host:x'), false)
|
|
assert.equal(Object.keys(byId).includes('action:'), false)
|
|
})
|
|
|
|
test('indexRootDashboardEntries indexes ACL-filtered dashboards', () => {
|
|
resetIndex()
|
|
|
|
indexRootDashboardEntries([
|
|
{ title: 'Actions', category: '' },
|
|
{ title: 'My Server', category: 'Infrastructure' },
|
|
{ title: '', category: 'Ignored' }
|
|
])
|
|
|
|
const byId = Object.fromEntries(searchIndexItems.value.map((item) => [item.id, item]))
|
|
assert.equal(byId['dashboard:Actions'].path, '/')
|
|
assert.equal(byId['dashboard:My Server'].path, '/dashboards/My Server')
|
|
assert.equal(byId['dashboard:My Server'].description, 'Infrastructure')
|
|
assert.equal(byId['dashboard:My Server'].category, 'Dashboards')
|
|
})
|
|
|
|
test('indexSearchHints replaces prior entity and action snapshots', () => {
|
|
resetIndex()
|
|
|
|
indexSearchHints({
|
|
entities: [{ title: 'old', type: 'host', uniqueKey: 'old' }],
|
|
actions: [{ title: 'Old Action', bindingId: 'old-action' }]
|
|
})
|
|
indexRootDashboardEntries([{ title: 'Old Board', category: '' }])
|
|
|
|
indexSearchHints({
|
|
entities: [{ title: 'new', type: 'host', uniqueKey: 'new' }],
|
|
actions: [{ title: 'New Action', bindingId: 'new-action' }]
|
|
})
|
|
indexRootDashboardEntries([{ title: 'New Board', category: 'Ops' }])
|
|
|
|
const byId = Object.fromEntries(searchIndexItems.value.map((item) => [item.id, item]))
|
|
assert.equal(byId['entity:host:old'], undefined)
|
|
assert.equal(byId['action:old-action'], undefined)
|
|
assert.equal(byId['dashboard:Old Board'], undefined)
|
|
assert.equal(byId['entity:host:new'].title, 'new')
|
|
assert.equal(byId['action:new-action'].title, 'New Action')
|
|
assert.equal(byId['dashboard:New Board'].description, 'Ops')
|
|
})
|
|
|
|
test('caps actions globally, entities per type, and dashboards from root entries', () => {
|
|
resetIndex()
|
|
|
|
const actions = []
|
|
for (let i = 0; i < MAX_SEARCH_HINT_ACTIONS + 20; i++) {
|
|
actions.push({ title: `Action ${i}`, bindingId: `a-${i}` })
|
|
}
|
|
|
|
const entities = []
|
|
for (let i = 0; i < MAX_SEARCH_HINT_ENTITIES_PER_TYPE + 20; i++) {
|
|
entities.push({ title: `Host ${i}`, type: 'host', uniqueKey: `${i}` })
|
|
entities.push({ title: `Container ${i}`, type: 'container', uniqueKey: `${i}` })
|
|
}
|
|
|
|
const dashboards = []
|
|
for (let i = 0; i < MAX_SEARCH_HINT_DASHBOARDS + 20; i++) {
|
|
dashboards.push({ title: `Board ${i}`, category: '' })
|
|
}
|
|
|
|
indexSearchHints({ actions, entities })
|
|
indexRootDashboardEntries(dashboards)
|
|
|
|
const items = searchIndexItems.value
|
|
assert.equal(items.filter((item) => item.category === 'Actions').length, MAX_SEARCH_HINT_ACTIONS)
|
|
assert.equal(items.filter((item) => item.category === 'Dashboards').length, MAX_SEARCH_HINT_DASHBOARDS)
|
|
assert.equal(
|
|
items.filter((item) => item.category === 'Entities' && item.description === 'host').length,
|
|
MAX_SEARCH_HINT_ENTITIES_PER_TYPE
|
|
)
|
|
assert.equal(
|
|
items.filter((item) => item.category === 'Entities' && item.description === 'container').length,
|
|
MAX_SEARCH_HINT_ENTITIES_PER_TYPE
|
|
)
|
|
})
|
|
|
|
test('indexSystemNavigation always includes entities and respects visibility flags', () => {
|
|
resetIndex()
|
|
|
|
indexSystemNavigation({
|
|
showLogs: true,
|
|
showDiagnostics: false,
|
|
entitiesTitle: 'Entities',
|
|
logsTitle: 'Logs',
|
|
diagnosticsTitle: 'Diagnostics'
|
|
})
|
|
|
|
let byId = Object.fromEntries(searchIndexItems.value.map((item) => [item.id, item]))
|
|
assert.equal(byId['nav:entities'].path, '/entities')
|
|
assert.equal(byId['nav:logs'].path, '/logs')
|
|
assert.equal(byId['nav:diagnostics'], undefined)
|
|
|
|
indexSystemNavigation({
|
|
showLogs: false,
|
|
showDiagnostics: true
|
|
})
|
|
|
|
byId = Object.fromEntries(searchIndexItems.value.map((item) => [item.id, item]))
|
|
assert.equal(byId['nav:entities'].path, '/entities')
|
|
assert.equal(byId['nav:logs'], undefined)
|
|
assert.equal(byId['nav:diagnostics'].path, '/diagnostics')
|
|
})
|
|
|
|
test('entities appear before actions in searchIndexItems', () => {
|
|
resetIndex()
|
|
|
|
indexSearchHints({
|
|
entities: [{ title: 'server1', type: 'host', uniqueKey: '0' }],
|
|
actions: [{ title: 'server1', bindingId: 'bind-server1' }]
|
|
})
|
|
|
|
const titlesByCategory = searchIndexItems.value.map((item) => [item.category, item.title])
|
|
const entityIndex = titlesByCategory.findIndex(([category]) => category === 'Entities')
|
|
const actionIndex = titlesByCategory.findIndex(([category]) => category === 'Actions')
|
|
|
|
assert.ok(entityIndex >= 0)
|
|
assert.ok(actionIndex >= 0)
|
|
assert.ok(entityIndex < actionIndex)
|
|
})
|
|
|
|
test('search index has no Logs category', () => {
|
|
resetIndex()
|
|
|
|
indexSearchHints({
|
|
entities: [{ title: 'web01', type: 'host', uniqueKey: '0' }],
|
|
actions: [{ title: 'Backup', bindingId: 'bind-backup' }]
|
|
})
|
|
indexRootDashboardEntries([{ title: 'Ops', category: '' }])
|
|
indexSystemNavigation({ showLogs: true, showDiagnostics: true })
|
|
|
|
assert.equal(
|
|
searchIndexItems.value.some((item) => item.category === 'Logs'),
|
|
false
|
|
)
|
|
})
|