Merge branch 'feat/plugin-mgmt-3-settings' into feat/plugin-mgmt-4-web
This commit is contained in:
commit
4264b1d9f2
6 files changed
+65
-14
No files matched your search
@@ -1,3 +1,4 @@
|
||||
import { stageStartupBundle } from './startup-bundle.ts'
|
||||
import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, symlinkSync, writeFileSync } from 'node:fs'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
@@ -411,7 +412,8 @@ describe.skipIf(!existsSync(dshBin))('dsh BUILT bin (node lib/bin.js, no tsx)',
|
||||
'',
|
||||
].join('\n'))
|
||||
try {
|
||||
const result = await runBuiltBin(['--profile', 'sdk', '--patch', patch], {
|
||||
stageStartupBundle(home, 'sdk', patch)
|
||||
const result = await runBuiltBin(['--profile', 'sdk'], {
|
||||
DSH_HOME: home,
|
||||
DSH_TELEMETRY_DISABLED: '1',
|
||||
DEEPSEEK_API_KEY: 'built-sdk-startup-failure-no-call',
|
||||
@@ -757,18 +759,20 @@ describe.skipIf(!existsSync(dshBin))('dsh BUILT bin (node lib/bin.js, no tsx)',
|
||||
}
|
||||
}, SPAWN_TIMEOUT_MS + 30_000)
|
||||
|
||||
it('keeps serving when an optional patch-overlay plugin fails', async () => {
|
||||
it('rejects an external bundle that corrupts a builtin provider', async () => {
|
||||
const home = mkdtempSync(join(tmpdir(), 'dsh-invalid-patch-'))
|
||||
try {
|
||||
const result = await runBuiltBin(['--profile', 'web', '--patch', invalidProvider, '--port', '0', '--no-open'], {
|
||||
stageStartupBundle(home, 'web', invalidProvider)
|
||||
const result = await runBuiltBin(['--profile', 'web', '--port', '0', '--no-open'], {
|
||||
DSH_HOME: home,
|
||||
DSH_BROWSER_OPEN_TEST_EXIT_ON_READY: '1',
|
||||
DEEPSEEK_API_KEY: 'keyless-invalid-config',
|
||||
DSH_TELEMETRY_DISABLED: '1',
|
||||
NODE_OPTIONS: `--import=${webReadyExitHook}`,
|
||||
})
|
||||
expect(result.code, result.stderr).toBe(0)
|
||||
expect(result.stdout).toMatch(/^dsh web: http:\/\/127\.0\.0\.1:\d+\/\?token=[A-Za-z0-9_-]+$/u)
|
||||
expect(result.code, result.stderr).toBe(1)
|
||||
expect(result.stdout).not.toContain('dsh web: http://')
|
||||
expect(result.stderr).toContain('required startup failure')
|
||||
expect(result.stderr).toContain('llm-pi-ai')
|
||||
} finally {
|
||||
rmSync(home, { recursive: true, force: true })
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
# Invalid optional provider override used to prove best-effort startup continues.
|
||||
# Invalid override of a builtin provider: an external patch retains the target's required startup policy.
|
||||
|
||||
- id: llm-pi-ai
|
||||
config:
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
scanZstdFrames,
|
||||
} from '@deepseek-ai/dsh-session-persistence-jsonl/src/zstd.ts'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { stageStartupBundle } from '../../../startup-bundle.ts'
|
||||
|
||||
const goldensDir = fileURLToPath(new URL('./expected/', import.meta.url))
|
||||
const goalScenarioDir = join(goldensDir, 'goal-tools')
|
||||
@@ -277,10 +278,10 @@ describe('headless stream-json snapshots', () => {
|
||||
tempDirPrefix: 'headless-snapshot-startup-error-',
|
||||
binScript: dshBinScript,
|
||||
configPath: startupFailureConfigPath,
|
||||
prepare: (cwd) => { stageStartupBundle(join(cwd, '.dsh'), 'headless', startupFailureConfigPath) },
|
||||
binArgs: [
|
||||
'--profile', 'headless',
|
||||
'--patch', headlessOverlayPath,
|
||||
'--patch', startupFailureConfigPath,
|
||||
'Complete the task despite the unrelated startup failure.',
|
||||
],
|
||||
tsconfigPath,
|
||||
|
||||
@@ -8,6 +8,7 @@ import type { Readable } from 'node:stream'
|
||||
import { fileURLToPath, pathToFileURL } from 'node:url'
|
||||
import { execa } from 'execa'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { stageStartupBundle } from '../../../startup-bundle.ts'
|
||||
|
||||
const repoRoot = fileURLToPath(new URL('../../../../../../', import.meta.url))
|
||||
const dshBin = join(repoRoot, 'apps/cli/lib/bin.js')
|
||||
@@ -46,6 +47,7 @@ function createFixture(): Fixture {
|
||||
'}',
|
||||
'',
|
||||
].join('\n'))
|
||||
writeFileSync(join(root, 'schema-failure.mjs'), 'export const Config = { "~standard": { version: 1, vendor: "startup", validate() { return { issues: [{ message: "web schema failure" }] } } } }; export function apply() {}\n')
|
||||
writeFileSync(join(root, 'sync-failure.mjs'), 'export function apply() { throw new Error("web sync apply failure") }\n')
|
||||
writeFileSync(join(root, 'async-failure.mjs'), [
|
||||
'export async function apply() {',
|
||||
@@ -61,10 +63,9 @@ function createFixture(): Fixture {
|
||||
].join('\n'))
|
||||
const patch = join(root, 'failures.patch.yml')
|
||||
writeFileSync(patch, [
|
||||
'- id: tool-todo',
|
||||
' disabled: false',
|
||||
' config: {}',
|
||||
'- insert:',
|
||||
' - id: web-probe-schema-failure',
|
||||
` name: ${pathToFileURL(join(root, 'schema-failure.mjs')).href}`,
|
||||
' - id: web-probe-good',
|
||||
` name: ${pathToFileURL(join(root, 'good.mjs')).href}`,
|
||||
' config:',
|
||||
@@ -134,10 +135,10 @@ async function waitForStartup(
|
||||
describe.skipIf(!builtArtifactsExist)('dsh Web profile best-effort startup', () => {
|
||||
it('serves the full Web app while unrelated entries fail to start', async () => {
|
||||
const fixture = createFixture()
|
||||
stageStartupBundle(fixture.home, 'web', fixture.patch)
|
||||
const child = execa(process.execPath, [
|
||||
dshBin,
|
||||
'--profile', 'web',
|
||||
'--patch', fixture.patch,
|
||||
'--no-open',
|
||||
'--port', '0',
|
||||
], {
|
||||
@@ -169,7 +170,7 @@ describe.skipIf(!builtArtifactsExist)('dsh Web profile best-effort startup', ()
|
||||
expect(html).toContain('__DSH_BOOT__')
|
||||
expect(readFileSync(fixture.events, 'utf8')).toBe('good apply\n')
|
||||
expect(startup.stderr).toContain('web-probe-import-failure')
|
||||
expect(startup.stderr).toContain('@deepseek-ai/dsh-tool-todo')
|
||||
expect(startup.stderr).toContain('web schema failure')
|
||||
expect(startup.stderr).toContain('web sync apply failure')
|
||||
expect(startup.stderr).toContain('web async apply failure')
|
||||
expect(startup.stderr).toContain('pending (waiting for service: webProbeMissingService)')
|
||||
@@ -316,11 +317,11 @@ describe.skipIf(!builtArtifactsExist)('dsh Web profile best-effort startup', ()
|
||||
` name: ${pathToFileURL(plugin).href}`,
|
||||
'',
|
||||
].join('\n'))
|
||||
stageStartupBundle(fixture.home, 'web', fixture.patch)
|
||||
try {
|
||||
const result = await execa(process.execPath, [
|
||||
dshBin,
|
||||
'--profile', 'web',
|
||||
'--patch', fixture.patch,
|
||||
'--no-open',
|
||||
'--port', '0',
|
||||
], {
|
||||
|
||||
@@ -8,6 +8,7 @@ import { fileURLToPath, pathToFileURL } from 'node:url'
|
||||
import { execa } from 'execa'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { FiberState } from '@deepseek-ai/cordis'
|
||||
import { stageStartupBundle } from '../../../startup-bundle.ts'
|
||||
|
||||
const repoRoot = fileURLToPath(new URL('../../../../../../', import.meta.url))
|
||||
const bin = join(repoRoot, 'apps/cli/lib/bin.js')
|
||||
@@ -172,6 +173,8 @@ describe.skipIf(!built)('Web process failure matrix', () => {
|
||||
it.each(failures)(`${required ? 'required' : 'optional'} startup %s`, async (failure, diagnostic) => {
|
||||
const f = fixture()
|
||||
writeFileSync(f.patch, f.render(id, failure))
|
||||
stageStartupBundle(f.home, 'web', f.patch, required ? 'boot' : 'runtime')
|
||||
writeFileSync(f.patch, '[]\n')
|
||||
const app = start(f)
|
||||
try {
|
||||
if (required) {
|
||||
@@ -360,7 +363,11 @@ describe.skipIf(!built)('Web process failure matrix', () => {
|
||||
const patch = JSON.stringify([{ insert: [f.observer, f.witness(0), {
|
||||
id: 'matrix-optional-http', name: pathToFileURL(plugin).href, config: { port: address.port },
|
||||
}] }]) + '\n'
|
||||
if (phase === 'startup') writeFileSync(f.patch, patch)
|
||||
if (phase === 'startup') {
|
||||
writeFileSync(f.patch, patch)
|
||||
stageStartupBundle(f.home, 'web', f.patch)
|
||||
writeFileSync(f.patch, '[]\n')
|
||||
}
|
||||
app = start(f)
|
||||
const running = app
|
||||
await app.serves()
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/** Installed bundle provenance for product-profile startup acceptance tests. */
|
||||
import { existsSync, mkdirSync, writeFileSync } from 'node:fs'
|
||||
import { join } from 'node:path'
|
||||
import {
|
||||
initProfile, loadOverlayPatches, PROFILE_TEMPLATES, readProfileManifest, writeProfileManifest, type ProfileLayer,
|
||||
} from '@deepseek-ai/dsh-app-boot'
|
||||
|
||||
/**
|
||||
* Install a patch as a real profile dependency, preserving its module resolution anchors.
|
||||
* @param home - the scenario's isolated Harness home.
|
||||
* @param profile - the shipped profile under test.
|
||||
* @param patchFile - the fixture patch whose relative module names resolve at its source.
|
||||
* @param stage - whether this external bundle is required or optional at startup.
|
||||
*/
|
||||
export function stageStartupBundle(
|
||||
home: string,
|
||||
profile: 'web' | 'sdk' | 'headless',
|
||||
patchFile: string,
|
||||
stage: ProfileLayer['stage'] = 'runtime',
|
||||
): void {
|
||||
const dir = join(home, 'profiles', profile)
|
||||
const template = PROFILE_TEMPLATES[profile]
|
||||
if (template === undefined) throw new Error(`Missing shipped profile template: ${profile}`)
|
||||
if (!existsSync(join(dir, 'package.json'))) initProfile(dir, template.bundles, template.patchReload)
|
||||
const name = '@fixture/startup-policy'
|
||||
const bundle = join(dir, 'node_modules', name)
|
||||
mkdirSync(bundle, { recursive: true })
|
||||
writeFileSync(join(bundle, 'package.json'), JSON.stringify({
|
||||
name, version: '1.0.0', private: true, type: 'module', dsh: { bundle: { patch: './cordis.patch.json', stage } },
|
||||
}))
|
||||
writeFileSync(join(bundle, 'cordis.patch.json'), JSON.stringify(loadOverlayPatches('startup fixture', patchFile)))
|
||||
const manifest = readProfileManifest('startup fixture', dir)
|
||||
writeProfileManifest(dir, {
|
||||
...manifest,
|
||||
dependencies: { ...manifest.dependencies, [name]: '1.0.0' },
|
||||
dsh: { ...manifest.dsh, profile: { ...manifest.dsh?.profile, bundles: [...manifest.dsh?.profile?.bundles ?? [], name] } },
|
||||
})
|
||||
}
|
||||
Reference in new issue
Block a user