mirror of
https://github.com/stablyai/orca.git
synced 2026-10-31 00:57:45 +00:00
test: tighten the round-2 parity, serve and watcher tests (#30267)
Parity test: one string-skip helper, no infinite loop on an unterminated block comment, no internal shorthand. Serve test: orcadServeArgs takes a bindHost instead of the test splicing argv, typed readiness, and stderr back in the daemon failure. Watcher test: a sentinel at both watched levels.
This commit is contained in:
@@ -73,9 +73,9 @@ describe('filesystem watcher native ignores', () => {
|
||||
sourceFiles.push(join(root, 'source\nnewline.ts'), join(nestedSource, 'source\nnewline.ts'))
|
||||
}
|
||||
await writeUntilSeen(sourceFiles)
|
||||
// Why: events arrive in order, so once a file written after the storm is seen, any
|
||||
// Why: events arrive in order, so once a file written after the storm is seen at both levels, any
|
||||
// unignored generated event is already recorded. Replaces a fixed debounce sleep.
|
||||
await writeUntilSeen([join(nestedSource, 'sentinel.ts')])
|
||||
await writeUntilSeen([join(root, 'sentinel.ts'), join(nestedSource, 'sentinel.ts')])
|
||||
|
||||
expect(errors).toEqual([])
|
||||
const generatedPaths = new Set(generatedFiles)
|
||||
|
||||
@@ -203,11 +203,14 @@ describe.skipIf(skip)('orca serve on orcad', () => {
|
||||
const appRoot = profile()
|
||||
mkdirSync(join(appRoot, 'out', 'web'), { recursive: true })
|
||||
writeFileSync(join(appRoot, 'out', 'web', WEB_CLIENT_INDEX_FILENAME), '<title>orca web</title>')
|
||||
// The argv `orca serve` builds, but on loopback: a test must not listen on every interface.
|
||||
const args = orcadServeArgs({ json: true, port: '0' }, findBundledWebClientRoot(appRoot))
|
||||
args.splice(args.indexOf('0.0.0.0'), 1, '127.0.0.1')
|
||||
// The argv `orca serve` builds, but on loopback.
|
||||
const args = orcadServeArgs(
|
||||
{ json: true, port: '0' },
|
||||
findBundledWebClientRoot(appRoot),
|
||||
'127.0.0.1'
|
||||
)
|
||||
try {
|
||||
const readiness = await serveOnce(userData, args)
|
||||
const { readiness } = await serveOnce(userData, args)
|
||||
const webClientUrl: unknown = readiness.pairing?.webClientUrl
|
||||
expect(webClientUrl).toEqual(expect.stringMatching(/^http:\/\/127\.0\.0\.1:\d+\//u))
|
||||
const index = await fetch(new URL(`/${WEB_CLIENT_INDEX_FILENAME}`, String(webClientUrl)))
|
||||
@@ -262,14 +265,30 @@ const running = new Map<string, ReturnType<typeof spawnProcess>>()
|
||||
|
||||
/** Starts `orca serve --json` on orcad and returns the terminal daemon pid its readiness names. */
|
||||
async function serveDaemonPid(userData: string): Promise<number> {
|
||||
const readiness = await serveOnce(userData, ['--bind', '127.0.0.1', '--port', '0', '--json'])
|
||||
const { readiness, stderr } = await serveOnce(userData, [
|
||||
'--bind',
|
||||
'127.0.0.1',
|
||||
'--port',
|
||||
'0',
|
||||
'--json'
|
||||
])
|
||||
const daemon = readiness.health?.terminalDaemon
|
||||
expect(daemon?.state).toBe('live')
|
||||
if (daemon?.state !== 'live') {
|
||||
throw new Error(`terminal daemon not live (${String(daemon?.state)}): ${stderr}`)
|
||||
}
|
||||
return daemon.pid
|
||||
}
|
||||
|
||||
/** Starts orcad with `args` and returns its parsed readiness line. */
|
||||
async function serveOnce(userData: string, args: string[]) {
|
||||
type ServeReadiness = {
|
||||
pairing?: { webClientUrl?: unknown }
|
||||
health?: { terminalDaemon?: { state?: string; pid: number } }
|
||||
}
|
||||
|
||||
/** Starts orcad with `args` and returns its parsed readiness line and stderr so far. */
|
||||
async function serveOnce(
|
||||
userData: string,
|
||||
args: string[]
|
||||
): Promise<{ readiness: ServeReadiness; stderr: string }> {
|
||||
const child = spawnProcess({
|
||||
program: runtime!,
|
||||
args: [join(slotDir, 'orcad.js'), ...args],
|
||||
@@ -291,7 +310,8 @@ async function serveOnce(userData: string, args: string[]) {
|
||||
}
|
||||
})
|
||||
})
|
||||
return JSON.parse(line)
|
||||
const readiness: ServeReadiness = JSON.parse(line)
|
||||
return { readiness, stderr }
|
||||
}
|
||||
|
||||
/** SIGTERM is serve's graceful stop on POSIX; it leaves the daemon running by design. */
|
||||
|
||||
@@ -7,7 +7,7 @@ import { SKILL_INSTALL_CAPABILITY } from '../../../shared/skill-install-capabili
|
||||
|
||||
/**
|
||||
* Electron (`main-process-*`) and orcad (`orcad-entry.ts`) each compose the same runtime, and every
|
||||
* dependency is optional, so one root can silently skip a service the other wires (C2: #30120 never
|
||||
* dependency is optional, so one root can silently skip a service the other wires (#30120 never
|
||||
* gave orcad its web client root). This records what each root's source wires and fails on any
|
||||
* difference that is not declared below with a reason.
|
||||
*/
|
||||
@@ -18,7 +18,7 @@ type DeclaredDifference = {
|
||||
wiring: string
|
||||
missingOn: Root
|
||||
reason: string
|
||||
/** Capabilities the missing host still advertises; its methods must refuse accurately (D3). */
|
||||
/** Capabilities the missing host still advertises; its methods must refuse accurately. */
|
||||
advertisedUnserved?: readonly string[]
|
||||
}
|
||||
|
||||
@@ -148,29 +148,37 @@ const SPREAD_SOURCES: Record<string, { file: string; anchor: string }> = {
|
||||
const repoRoot = join(__dirname, '../../../..')
|
||||
const read = (file: string): string => stripComments(readFileSync(join(repoRoot, file), 'utf8'))
|
||||
|
||||
const QUOTES = `'"\``
|
||||
|
||||
/** The index of the quote that closes the string opening at `open` (or the end of the source). */
|
||||
function stringEnd(source: string, open: number): number {
|
||||
for (let i = open + 1; i < source.length; i++) {
|
||||
if (source[i] === '\\') {
|
||||
i++
|
||||
} else if (source[i] === source[open]) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return source.length
|
||||
}
|
||||
|
||||
function stripComments(source: string): string {
|
||||
let out = ''
|
||||
let quote: string | null = null
|
||||
for (let i = 0; i < source.length; i++) {
|
||||
const char = source[i]
|
||||
if (quote) {
|
||||
out += char
|
||||
if (char === '\\') {
|
||||
out += source[++i] ?? ''
|
||||
} else if (char === quote) {
|
||||
quote = null
|
||||
}
|
||||
if (QUOTES.includes(char)) {
|
||||
const end = stringEnd(source, i)
|
||||
out += source.slice(i, end + 1)
|
||||
i = end
|
||||
} else if (char === '/' && source[i + 1] === '/') {
|
||||
while (i < source.length && source[i] !== '\n') {
|
||||
i++
|
||||
}
|
||||
out += '\n'
|
||||
} else if (char === '/' && source[i + 1] === '*') {
|
||||
i = source.indexOf('*/', i + 2) + 1
|
||||
const end = source.indexOf('*/', i + 2)
|
||||
i = end === -1 ? source.length : end + 1
|
||||
} else {
|
||||
if (char === "'" || char === '"' || char === '`') {
|
||||
quote = char
|
||||
}
|
||||
out += char
|
||||
}
|
||||
}
|
||||
@@ -180,19 +188,10 @@ function stripComments(source: string): string {
|
||||
/** The inner text of the brace group that opens at `open` (strings are opaque). */
|
||||
function braceGroup(source: string, open: number): string {
|
||||
let depth = 0
|
||||
let quote: string | null = null
|
||||
for (let i = open; i < source.length; i++) {
|
||||
const char = source[i]
|
||||
if (quote) {
|
||||
if (char === '\\') {
|
||||
i++
|
||||
} else if (char === quote) {
|
||||
quote = null
|
||||
}
|
||||
continue
|
||||
}
|
||||
if (char === "'" || char === '"' || char === '`') {
|
||||
quote = char
|
||||
if (QUOTES.includes(char)) {
|
||||
i = stringEnd(source, i)
|
||||
} else if (char === '{') {
|
||||
depth++
|
||||
} else if (char === '}' && --depth === 0) {
|
||||
@@ -218,20 +217,11 @@ function objectLiteralAfter(source: string, anchor: string): string {
|
||||
function topLevelEntries(inner: string): string[] {
|
||||
const entries: string[] = []
|
||||
let depth = 0
|
||||
let quote: string | null = null
|
||||
let start = 0
|
||||
for (let i = 0; i < inner.length; i++) {
|
||||
const char = inner[i]
|
||||
if (quote) {
|
||||
if (char === '\\') {
|
||||
i++
|
||||
} else if (char === quote) {
|
||||
quote = null
|
||||
}
|
||||
continue
|
||||
}
|
||||
if (char === "'" || char === '"' || char === '`') {
|
||||
quote = char
|
||||
if (QUOTES.includes(char)) {
|
||||
i = stringEnd(inner, i)
|
||||
} else if ('({['.includes(char)) {
|
||||
depth++
|
||||
} else if (')}]'.includes(char)) {
|
||||
|
||||
@@ -31,10 +31,15 @@ export function serveOptionArgs(args: ServeOrcaAppArgs, prefix: '--' | '--serve-
|
||||
]
|
||||
}
|
||||
|
||||
export function orcadServeArgs(args: ServeOrcaAppArgs, webClientRoot?: string): string[] {
|
||||
/** `bindHost` exists for tests, which must not listen on every interface. */
|
||||
export function orcadServeArgs(
|
||||
args: ServeOrcaAppArgs,
|
||||
webClientRoot?: string,
|
||||
bindHost = '0.0.0.0'
|
||||
): string[] {
|
||||
return [
|
||||
'--bind',
|
||||
'0.0.0.0',
|
||||
bindHost,
|
||||
...serveOptionArgs(args, '--'),
|
||||
...(webClientRoot ? ['--web-client-root', webClientRoot] : [])
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user