Refine desktop setup and remove bundled app center apps
This commit is contained in:
214
tests/unit/yinian-initializer.test.ts
Normal file
214
tests/unit/yinian-initializer.test.ts
Normal file
@@ -0,0 +1,214 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
import { tmpdir } from 'node:os';
|
||||
import {
|
||||
YINIAN_MODEL_AUTH_PROFILE_ID,
|
||||
YINIAN_MODEL_PROVIDER_KEY,
|
||||
YINIAN_MODEL_REF,
|
||||
} from '../../shared/yinian-model';
|
||||
|
||||
const initializerMocks = vi.hoisted(() => ({
|
||||
configDir: '',
|
||||
runtimeDir: '',
|
||||
settings: {} as Record<string, unknown>,
|
||||
setSetting: vi.fn(async (key: string, value: unknown) => {
|
||||
initializerMocks.settings[key] = value;
|
||||
}),
|
||||
ensureOfficeSkillRuntimeReady: vi.fn(async () => ({
|
||||
ok: true,
|
||||
checks: [
|
||||
{ id: 'python', label: 'Python 运行环境', status: 'ok', detail: '/tmp/python' },
|
||||
],
|
||||
python: {
|
||||
executable: '/tmp/python',
|
||||
packages: [],
|
||||
},
|
||||
dotnet: {
|
||||
available: false,
|
||||
version: null,
|
||||
},
|
||||
})),
|
||||
}));
|
||||
|
||||
vi.mock('@electron/utils/store', () => ({
|
||||
getAllSettings: vi.fn(async () => initializerMocks.settings),
|
||||
setSetting: initializerMocks.setSetting,
|
||||
}));
|
||||
|
||||
vi.mock('@electron/utils/paths', () => ({
|
||||
getOpenClawConfigDir: () => initializerMocks.configDir,
|
||||
reinstallManagedOpenClawRuntime: vi.fn(() => ({
|
||||
source: 'bundled',
|
||||
dir: initializerMocks.runtimeDir,
|
||||
})),
|
||||
}));
|
||||
|
||||
vi.mock('@electron/utils/office-skill-runtime', () => ({
|
||||
ensureOfficeSkillRuntimeReady: initializerMocks.ensureOfficeSkillRuntimeReady,
|
||||
}));
|
||||
|
||||
vi.mock('@electron/utils/logger', () => ({
|
||||
logger: {
|
||||
info: vi.fn(),
|
||||
warn: vi.fn(),
|
||||
error: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
function writeRuntimeFiles(runtimeDir: string): void {
|
||||
const files = [
|
||||
'package.json',
|
||||
'openclaw.mjs',
|
||||
join('docs', 'reference', 'templates', 'SOUL.md'),
|
||||
join('docs', 'reference', 'templates', 'IDENTITY.md'),
|
||||
join('docs', 'reference', 'templates', 'USER.md'),
|
||||
join('docs', 'reference', 'templates', 'AGENTS.md'),
|
||||
join('docs', 'reference', 'templates', 'TOOLS.md'),
|
||||
join('docs', 'reference', 'templates', 'HEARTBEAT.md'),
|
||||
join('docs', 'reference', 'templates', 'BOOT.md'),
|
||||
join('node_modules', 'openclaw', 'package.json'),
|
||||
];
|
||||
|
||||
for (const file of files) {
|
||||
const filePath = join(runtimeDir, file);
|
||||
mkdirSync(join(filePath, '..'), { recursive: true });
|
||||
writeFileSync(filePath, '{}');
|
||||
}
|
||||
}
|
||||
|
||||
function writeInternalAuthManifest(rootDir: string, manifest: unknown): void {
|
||||
const manifestPath = join(rootDir, 'build', 'yinian-internal', 'model-auth-profiles.json');
|
||||
mkdirSync(join(manifestPath, '..'), { recursive: true });
|
||||
writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, 'utf8');
|
||||
}
|
||||
|
||||
describe('Yinian initializer', () => {
|
||||
const originalCwd = process.cwd();
|
||||
const originalHome = process.env.HOME;
|
||||
let testRoot = '';
|
||||
|
||||
beforeEach(() => {
|
||||
vi.resetModules();
|
||||
vi.clearAllMocks();
|
||||
initializerMocks.settings = {};
|
||||
testRoot = join(tmpdir(), `yinian-init-${Date.now()}-${Math.random().toString(16).slice(2)}`);
|
||||
initializerMocks.configDir = join(testRoot, '.openclaw');
|
||||
initializerMocks.runtimeDir = join(initializerMocks.configDir, 'runtime', 'openclaw');
|
||||
mkdirSync(testRoot, { recursive: true });
|
||||
process.env.HOME = testRoot;
|
||||
process.chdir(testRoot);
|
||||
writeRuntimeFiles(initializerMocks.runtimeDir);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
process.chdir(originalCwd);
|
||||
process.env.HOME = originalHome;
|
||||
rmSync(testRoot, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
it('skips bundled model seeding for non-pilot packages and completes initialization', async () => {
|
||||
writeInternalAuthManifest(testRoot, {
|
||||
bundled: false,
|
||||
reason: 'YINIAN_BUNDLE_MODEL_AUTH is not enabled',
|
||||
});
|
||||
const { initializeYinianRuntime } = await import('@electron/utils/yinian-initializer');
|
||||
|
||||
const result = await initializeYinianRuntime();
|
||||
|
||||
expect(result.initialized).toBe(true);
|
||||
expect(result.steps.find((step) => step.id === 'model')).toMatchObject({
|
||||
status: 'success',
|
||||
message: '模型 API 可在设置中配置',
|
||||
});
|
||||
expect(result.steps.find((step) => step.id === 'python')).toMatchObject({
|
||||
status: 'success',
|
||||
});
|
||||
const config = JSON.parse(readFileSync(join(initializerMocks.configDir, 'openclaw.json'), 'utf8')) as {
|
||||
agents?: { defaults?: { model?: unknown; workspace?: string } };
|
||||
models?: { providers?: Record<string, unknown>; mode?: string };
|
||||
};
|
||||
expect(config.models?.mode).toBe('merge');
|
||||
expect(config.models?.providers).toEqual({});
|
||||
expect(config.agents?.defaults?.model).toBeUndefined();
|
||||
expect(initializerMocks.ensureOfficeSkillRuntimeReady).toHaveBeenCalled();
|
||||
expect(initializerMocks.setSetting).toHaveBeenCalledWith('setupComplete', true);
|
||||
});
|
||||
|
||||
it('skips legacy bundled credentials without explicit model runtime config', async () => {
|
||||
writeInternalAuthManifest(testRoot, {
|
||||
bundled: true,
|
||||
store: {
|
||||
version: 1,
|
||||
profiles: {
|
||||
'minimax:default': {
|
||||
type: 'api_key',
|
||||
provider: 'minimax',
|
||||
key: 'test-secret-key',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const { initializeYinianRuntime } = await import('@electron/utils/yinian-initializer');
|
||||
|
||||
const result = await initializeYinianRuntime();
|
||||
|
||||
expect(result.initialized).toBe(true);
|
||||
expect(result.steps.find((step) => step.id === 'model')).toMatchObject({
|
||||
status: 'success',
|
||||
message: '模型 API 可在设置中配置',
|
||||
});
|
||||
const configPath = join(initializerMocks.configDir, 'openclaw.json');
|
||||
const config = JSON.parse(readFileSync(configPath, 'utf8')) as {
|
||||
models?: { providers?: Record<string, unknown> };
|
||||
agents?: { defaults?: { model?: unknown } };
|
||||
};
|
||||
expect(config.models?.providers).toEqual({});
|
||||
expect(config.agents?.defaults?.model).toBeUndefined();
|
||||
expect(initializerMocks.ensureOfficeSkillRuntimeReady).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('seeds bundled pilot credentials and completes initialization', async () => {
|
||||
writeInternalAuthManifest(testRoot, {
|
||||
bundled: true,
|
||||
model: {
|
||||
providerKey: YINIAN_MODEL_PROVIDER_KEY,
|
||||
modelId: 'custom-model',
|
||||
modelName: 'Custom Model',
|
||||
baseUrl: 'https://api.example.com/v1',
|
||||
api: 'openai-completions',
|
||||
authProfileId: YINIAN_MODEL_AUTH_PROFILE_ID,
|
||||
},
|
||||
store: {
|
||||
version: 1,
|
||||
profiles: {
|
||||
[YINIAN_MODEL_AUTH_PROFILE_ID]: {
|
||||
type: 'api_key',
|
||||
provider: YINIAN_MODEL_PROVIDER_KEY,
|
||||
key: 'test-secret-key',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const { initializeYinianRuntime } = await import('@electron/utils/yinian-initializer');
|
||||
|
||||
const result = await initializeYinianRuntime();
|
||||
|
||||
expect(result.initialized).toBe(true);
|
||||
expect(result.steps.find((step) => step.id === 'model')).toMatchObject({
|
||||
status: 'success',
|
||||
message: YINIAN_MODEL_REF,
|
||||
});
|
||||
expect(result.steps.find((step) => step.id === 'python')).toMatchObject({
|
||||
status: 'success',
|
||||
});
|
||||
const authProfiles = JSON.parse(readFileSync(
|
||||
join(initializerMocks.configDir, 'agents', 'main', 'agent', 'auth-profiles.json'),
|
||||
'utf8',
|
||||
)) as { profiles: Record<string, { provider?: string }> };
|
||||
expect(authProfiles.profiles[YINIAN_MODEL_AUTH_PROFILE_ID]).toMatchObject({
|
||||
provider: YINIAN_MODEL_PROVIDER_KEY,
|
||||
});
|
||||
expect(initializerMocks.setSetting).toHaveBeenCalledWith('setupComplete', true);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user