refactor(workspace): enforce layered crate boundaries

This commit is contained in:
elky
2026-07-15 23:47:19 +08:00
parent a728c090a9
commit 8616fe6ee2
969 changed files with 40187 additions and 27240 deletions
@@ -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()])')
})
})