mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-09 18:59:50 +08:00
refactor(workspace): enforce layered crate boundaries
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { resolve } from 'node:path'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
const source = readFileSync(
|
||||
resolve(process.cwd(), 'src/views/admin/AdminOperationsDashboard.vue'),
|
||||
'utf8',
|
||||
)
|
||||
|
||||
describe('AdminOperationsDashboard manual refresh', () => {
|
||||
it('only starts a refresh from the explicit refresh button', () => {
|
||||
expect(source).toContain('@click="refreshAll()"')
|
||||
expect(source).not.toContain('onMounted(')
|
||||
expect(source).not.toContain('setInterval(')
|
||||
expect(source).not.toContain('visibilitychange')
|
||||
|
||||
const rangeWatcher = source
|
||||
.split('watch(timeRange, () => {')[1]
|
||||
?.split('}, { deep: true })')[0]
|
||||
expect(rangeWatcher).toBeTruthy()
|
||||
expect(rangeWatcher).not.toContain('refreshAll(')
|
||||
})
|
||||
|
||||
it('does not request the heavyweight system-status fallback', () => {
|
||||
expect(source).not.toContain('monitoringApi.getSystemStatus()')
|
||||
})
|
||||
|
||||
it('forces fresh analytics and renders each result as soon as it settles', () => {
|
||||
expect(source).toContain('adminApi.getTimeSeries(params, { skipCache: true })')
|
||||
expect(source).toContain('adminApi.getPercentiles(params, { skipCache: true })')
|
||||
expect(source).toContain('}, { skipCache: true })')
|
||||
expect(source).toContain('adminApi.getErrorDistribution(params, { skipCache: true })')
|
||||
expect(source).toContain('include_timeline: false')
|
||||
|
||||
const progressiveSetup = source.split('const results = await Promise.allSettled([')[0]
|
||||
expect(progressiveSetup).toContain('timeSeries.value = value')
|
||||
expect(progressiveSetup).toContain('percentiles.value = value')
|
||||
expect(progressiveSetup).toContain('providerPerformance.value = value')
|
||||
expect(progressiveSetup).toContain('errorDistribution.value = value.distribution')
|
||||
expect(progressiveSetup).toContain('gatewayMetrics.value = value')
|
||||
})
|
||||
|
||||
it('reuses analytics responses instead of requesting a duplicate usage summary', () => {
|
||||
expect(source).not.toContain('usageApi.getUsageStats(')
|
||||
expect(source).not.toContain('summaryStats')
|
||||
expect(source).toContain('function seriesTokenTotal(')
|
||||
expect(source).toContain('numeric(item.input_tokens)')
|
||||
expect(source).toContain('numeric(item.output_tokens)')
|
||||
expect(source).toContain('numeric(item.cache_creation_tokens)')
|
||||
expect(source).toContain('numeric(item.cache_read_tokens)')
|
||||
expect(source).toContain('timeSeries.value.map(seriesTokenTotal)')
|
||||
expect(source).toContain('label="已分类错误"')
|
||||
expect(source).toContain('errorDistribution.value.reduce(')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,34 @@
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { resolve } from 'node:path'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
const source = readFileSync(
|
||||
resolve(process.cwd(), 'src/views/admin/CacheMonitoring.vue'),
|
||||
'utf8',
|
||||
)
|
||||
|
||||
describe('CacheMonitoring analysis loading', () => {
|
||||
it('observes the TTL section against the app scroll container', () => {
|
||||
expect(source).toContain('ref="analysisSectionRef"')
|
||||
expect(source).toContain("target.closest('.app-shell__content')")
|
||||
expect(source).toContain("rootMargin: '600px 0px'")
|
||||
})
|
||||
|
||||
it('keeps TTL and hit analysis out of the initial request group', () => {
|
||||
const mountedBlock = source
|
||||
.split('onMounted(() => {')[1]
|
||||
?.split('onBeforeUnmount')[0]
|
||||
|
||||
expect(mountedBlock).toBeTruthy()
|
||||
expect(mountedBlock).toContain('const initialLoad = refreshData()')
|
||||
expect(mountedBlock).not.toContain('refreshAnalysis()')
|
||||
expect(source).toContain('const waitForInitialData = initialDataPromise ?? Promise.resolve()')
|
||||
})
|
||||
|
||||
it('cleans up the observer when the page unmounts', () => {
|
||||
const unmountedBlock = source.split('onBeforeUnmount(() => {')[1]
|
||||
|
||||
expect(unmountedBlock).toBeTruthy()
|
||||
expect(unmountedBlock).toContain('stopAnalysisObserver?.()')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,41 @@
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { resolve } from 'node:path'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
const source = readFileSync(
|
||||
resolve(process.cwd(), 'src/views/admin/PerformanceAnalysis.vue'),
|
||||
'utf8',
|
||||
)
|
||||
|
||||
describe('PerformanceAnalysis progressive live data', () => {
|
||||
it('commits each live response before the full request group settles', () => {
|
||||
const liveDataBlock = source
|
||||
.split('async function loadLiveData')[1]
|
||||
?.split('const errorTrendChartData')[0]
|
||||
expect(liveDataBlock).toBeTruthy()
|
||||
expect(liveDataBlock).toContain('function commitIfCurrent<T>')
|
||||
expect(liveDataBlock).toContain('if (requestId !== liveRequestId) return')
|
||||
expect(liveDataBlock).toContain('systemStatus.value = value')
|
||||
expect(liveDataBlock).toContain('resilienceStatus.value = value')
|
||||
expect(liveDataBlock).toContain('circuitHistory.value = value.items')
|
||||
expect(liveDataBlock).toContain('gatewayMetrics.value = value')
|
||||
expect(liveDataBlock).toContain('liveReady.value = true')
|
||||
|
||||
expect(liveDataBlock).not.toContain('systemStatus.value = systemResult.value')
|
||||
expect(liveDataBlock).not.toContain('resilienceStatus.value = resilienceResult.value')
|
||||
expect(liveDataBlock).not.toContain('gatewayMetrics.value = metricsResult.value')
|
||||
})
|
||||
|
||||
it('keeps group completion for loading state and error aggregation', () => {
|
||||
const liveDataBlock = source
|
||||
.split('async function loadLiveData')[1]
|
||||
?.split('const errorTrendChartData')[0]
|
||||
expect(liveDataBlock).toContain('await Promise.allSettled([')
|
||||
expect(liveDataBlock).toContain("failedScopes.push('系统状态')")
|
||||
expect(liveDataBlock).toContain("failedScopes.push('韧性状态')")
|
||||
expect(liveDataBlock).toContain("failedScopes.push('熔断历史')")
|
||||
expect(liveDataBlock).toContain("failedScopes.push('网关指标')")
|
||||
expect(liveDataBlock).toContain('liveLoading.value = false')
|
||||
expect(liveDataBlock).toContain('liveRefreshing.value = false')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,28 @@
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { resolve } from 'node:path'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
const source = readFileSync(
|
||||
resolve(process.cwd(), 'src/views/admin/ProviderManagement.vue'),
|
||||
'utf8',
|
||||
)
|
||||
|
||||
describe('ProviderManagement detail drawer loading', () => {
|
||||
it('keeps the heavy detail drawer out of the initial route chunk', () => {
|
||||
expect(source).not.toContain(
|
||||
"import ProviderDetailDrawer from '@/features/providers/components/ProviderDetailDrawer.vue'",
|
||||
)
|
||||
expect(source).toContain(
|
||||
"() => import('@/features/providers/components/ProviderDetailDrawer.vue')",
|
||||
)
|
||||
})
|
||||
|
||||
it('does not resolve the async drawer until it is opened', () => {
|
||||
const drawerTemplate = source
|
||||
.split('<ProviderDetailDrawer')[1]
|
||||
?.split('/>')[0]
|
||||
|
||||
expect(drawerTemplate).toBeTruthy()
|
||||
expect(drawerTemplate).toContain('v-if="providerDrawerOpen"')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,56 @@
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { resolve } from 'node:path'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
const source = readFileSync(
|
||||
resolve(process.cwd(), 'src/views/admin/Users.vue'),
|
||||
'utf8',
|
||||
)
|
||||
|
||||
describe('Users request loading', () => {
|
||||
it('debounces search without delaying discrete filters', () => {
|
||||
const searchWatcher = source
|
||||
.split('watch(searchQuery, () => {')[1]
|
||||
?.split('watch([filterRole, filterStatus, filterGroup, sortOption]')[0]
|
||||
expect(searchWatcher).toBeTruthy()
|
||||
expect(searchWatcher).toContain('setTimeout(')
|
||||
expect(searchWatcher).toContain('USERS_SEARCH_DEBOUNCE_MS')
|
||||
|
||||
const discreteFilterWatcher = source
|
||||
.split('watch([filterRole, filterStatus, filterGroup, sortOption], () => {')[1]
|
||||
?.split('watch(paginatedUsers')[0]
|
||||
expect(discreteFilterWatcher).toBeTruthy()
|
||||
expect(discreteFilterWatcher).not.toContain('setTimeout(')
|
||||
expect(discreteFilterWatcher).toContain('refreshUsers()')
|
||||
})
|
||||
|
||||
it('does not reload invariant metadata for every list refresh', () => {
|
||||
const listRefresh = source
|
||||
.split('async function refreshUsers(')[1]
|
||||
?.split('async function handleManualRefresh()')[0]
|
||||
expect(listRefresh).toBeTruthy()
|
||||
expect(listRefresh).not.toContain('loadUserGroups()')
|
||||
expect(listRefresh).not.toContain('loadUserWallets(')
|
||||
|
||||
const manualRefresh = source
|
||||
.split('async function handleManualRefresh()')[1]
|
||||
?.split('function handleTableSort')[0]
|
||||
expect(manualRefresh).toBeTruthy()
|
||||
expect(manualRefresh).toContain('refreshUsers()')
|
||||
expect(manualRefresh).toContain('loadUserGroups()')
|
||||
expect(manualRefresh).toContain('loadUserWallets()')
|
||||
expect(source).toContain('@refresh="handleManualRefresh"')
|
||||
})
|
||||
|
||||
it('refreshes wallet state after user access-control mutations', () => {
|
||||
const batchCompleted = source
|
||||
.split('async function handleUserBatchCompleted')[1]
|
||||
?.split('function invalidateUserOptions')[0]
|
||||
expect(batchCompleted).toContain('Promise.all([refreshUsers(), loadUserWallets()])')
|
||||
|
||||
const formSubmit = source
|
||||
.split('async function handleUserFormSubmit')[1]
|
||||
?.split('async function manageApiKeys')[0]
|
||||
expect(formSubmit).toContain('Promise.all([refreshUsers(), loadUserWallets()])')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,24 @@
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { resolve } from 'node:path'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
const source = readFileSync(
|
||||
resolve(process.cwd(), 'src/views/admin/WalletsManagement.vue'),
|
||||
'utf8',
|
||||
)
|
||||
|
||||
describe('WalletsManagement wallet metadata loading', () => {
|
||||
it('only loads the full wallet metadata map for the orders tab', () => {
|
||||
const mountedBlock = source
|
||||
.split('onMounted(async () => {')[1]
|
||||
?.split('})')[0]
|
||||
expect(mountedBlock).toBeTruthy()
|
||||
expect(mountedBlock).not.toContain('loadWalletMetaMap()')
|
||||
|
||||
const ordersBranch = source
|
||||
.split("case 'orders':")[1]
|
||||
?.split("case 'callbacks':")[0]
|
||||
expect(ordersBranch).toBeTruthy()
|
||||
expect(ordersBranch).toContain('Promise.all([loadOrders(), loadWalletMetaMap()])')
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user