feat(editor): 新增 buildRunnableInfo 构造 runnable fence info string
纯函数,把语言+overrides 配置转成 'python runnable {...}' 形态。
dirty=false 省略 JSON,dirty=true 写 timeout/memory/network 三项,
字段顺序固定。
This commit is contained in:
parent
89bab26b90
commit
84b3da9db6
48
libs/tiptap-editor/src/__tests__/runnable-info.test.ts
Normal file
48
libs/tiptap-editor/src/__tests__/runnable-info.test.ts
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
import { buildRunnableInfo } from '../slash-command';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* buildRunnableInfo 测试:把弹框收集的配置转成 fence info string。
|
||||||
|
*
|
||||||
|
* dirty=false(全默认) → 'python runnable'(省略 JSON)
|
||||||
|
* dirty=true → 'python runnable {"timeout_secs":N,"memory_mb":M,"allow_network":B}'
|
||||||
|
* JSON 字段顺序固定:timeout → memory → network
|
||||||
|
*/
|
||||||
|
|
||||||
|
describe('buildRunnableInfo', () => {
|
||||||
|
const base = {
|
||||||
|
lang: 'python',
|
||||||
|
timeoutSecs: 5,
|
||||||
|
memoryMb: 256,
|
||||||
|
allowNetwork: false,
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
describe('dirty=false(全默认,省略 JSON)', () => {
|
||||||
|
it('python → "python runnable"', () => {
|
||||||
|
expect(buildRunnableInfo({ ...base, dirty: false })).toBe('python runnable');
|
||||||
|
});
|
||||||
|
it('node → "node runnable"', () => {
|
||||||
|
expect(buildRunnableInfo({ ...base, lang: 'node', dirty: false })).toBe('node runnable');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('dirty=true(写 JSON)', () => {
|
||||||
|
it('改了 timeout,JSON 含全部 3 项', () => {
|
||||||
|
expect(buildRunnableInfo({ ...base, timeoutSecs: 10, dirty: true })).toBe(
|
||||||
|
'python runnable {"timeout_secs":10,"memory_mb":256,"allow_network":false}',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('node + 改了 allow_network', () => {
|
||||||
|
expect(buildRunnableInfo({ ...base, lang: 'node', allowNetwork: true, dirty: true })).toBe(
|
||||||
|
'node runnable {"timeout_secs":5,"memory_mb":256,"allow_network":true}',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('JSON 字段顺序固定(timeout→memory→network)', () => {
|
||||||
|
const out = buildRunnableInfo({ ...base, dirty: true });
|
||||||
|
const jsonStart = out.indexOf('{');
|
||||||
|
const jsonEnd = out.lastIndexOf('}') + 1;
|
||||||
|
const keys = Object.keys(JSON.parse(out.slice(jsonStart, jsonEnd)));
|
||||||
|
expect(keys).toEqual(['timeout_secs', 'memory_mb', 'allow_network']);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -363,3 +363,34 @@ function createPopup(props: SuggestionProps<CommandItem>): SlashPopup {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** buildRunnableInfo 的输入配置。 */
|
||||||
|
export interface RunnableInfoOpts {
|
||||||
|
/** 语言名(python / node)。 */
|
||||||
|
lang: string;
|
||||||
|
/** 超时秒数。 */
|
||||||
|
timeoutSecs: number;
|
||||||
|
/** 内存上限(MB)。 */
|
||||||
|
memoryMb: number;
|
||||||
|
/** 是否允许网络。 */
|
||||||
|
allowNetwork: boolean;
|
||||||
|
/** 作者是否改动过任一 overrides 字段;false 则省略 JSON。 */
|
||||||
|
dirty: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 把弹框收集的配置转成 markdown fence 的 info string。
|
||||||
|
*
|
||||||
|
* - dirty=false → `${lang} runnable`(省略 JSON,最小形态)
|
||||||
|
* - dirty=true → `${lang} runnable {"timeout_secs":N,"memory_mb":M,"allow_network":B}`
|
||||||
|
*
|
||||||
|
* JSON 字段顺序固定(timeout → memory → network),由显式构造保证(不依赖对象插入顺序)。
|
||||||
|
* 到达此函数时值必然合法(弹框「插入」按钮在非法值时 disabled)。
|
||||||
|
*/
|
||||||
|
export function buildRunnableInfo(opts: RunnableInfoOpts): string {
|
||||||
|
const prefix = `${opts.lang} runnable`;
|
||||||
|
if (!opts.dirty) return prefix;
|
||||||
|
// 显式拼字符串,保证字段顺序固定(timeout → memory → network),不依赖对象键序。
|
||||||
|
const json = `{"timeout_secs":${opts.timeoutSecs},"memory_mb":${opts.memoryMb},"allow_network":${opts.allowNetwork}}`;
|
||||||
|
return `${prefix} ${json}`;
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user