diff --git a/docs/release-control/v6/internal/subsystems/deployment-installability.md b/docs/release-control/v6/internal/subsystems/deployment-installability.md index cc76794db9..53353260fd 100644 --- a/docs/release-control/v6/internal/subsystems/deployment-installability.md +++ b/docs/release-control/v6/internal/subsystems/deployment-installability.md @@ -7781,3 +7781,20 @@ or failed parsers, failed transport with a plausible body, redirects, pins and downgrade refusal. Existing extracted-function tests include the shared selector. These are source-level selection controls, not native installation, published release acceptance or a repair to a third-party community helper. + + +### Combined-batch native fixture reconciliation (11 October 2026) + +The real provider-MSP startup fixture is Linux-only at compile time, matching +its existing Linux transport admission. Its cloud-control-plane import must not +pull Unix filesystem types into Windows installer test compilation. Windows +installer and lifecycle tests remain enabled; the startup fixture still runs in +the Linux process proof and hosted Compose job. + +Successful command stdout alone supplies container identities, published ports +and bootstrap facts. Stderr warnings remain logged evidence, never arguments to +the next Docker command. Failure diagnostics retain both streams and the original +nonzero exit. `TestProviderMSPStartupCommandSeparatesDiagnostics` checks three +consumer shapes with controlled successful stderr; it is not Docker acceptance. +Hosted mounted-secret/readiness/persistence/refusal/cleanup checks remain required. +Frontend fixture reformatting retains all assertions and changes no runtime UI. diff --git a/frontend-modern/src/features/docs/__tests__/agentTokenFileGuide.test.ts b/frontend-modern/src/features/docs/__tests__/agentTokenFileGuide.test.ts index 73d72364e2..eeff5e1ae2 100644 --- a/frontend-modern/src/features/docs/__tests__/agentTokenFileGuide.test.ts +++ b/frontend-modern/src/features/docs/__tests__/agentTokenFileGuide.test.ts @@ -25,7 +25,9 @@ describe('agent token-file help in the shipped Markdown renderer', () => { expect(link?.getAttribute('href')).toBe(`#${id}`); expect(link?.hasAttribute('data-doc-link')).toBe(false); expect(link?.hasAttribute('target')).toBe(false); - expect(article.querySelector(`#${id}`)?.textContent).toBe('Token-file format is not shell syntax'); + expect(article.querySelector(`#${id}`)?.textContent).toBe( + 'Token-file format is not shell syntax', + ); expect(guide).toBe( readFileSync(path.join(repoRoot, 'frontend-modern/public/docs/UNIFIED_AGENT.md'), 'utf8'), ); @@ -55,10 +57,15 @@ describe('agent token-file help in the shipped Markdown renderer', () => { ].entries()) { expect(rows[index].textContent).toContain(phrase); } - const text = parts.map((element) => element.textContent).join(' ').replace(/\s+/g, ' '); + const text = parts + .map((element) => element.textContent) + .join(' ') + .replace(/\s+/g, ' '); expect(text).toContain('not the value, length, hint, screenshot or file'); expect(text).toContain('Do not print, source, upload or edit the installed token file'); expect(text).toContain('not proof of accepted reporting'); - expect(parts.some((element) => element.matches('pre') || element.querySelector('pre'))).toBe(false); + expect(parts.some((element) => element.matches('pre') || element.querySelector('pre'))).toBe( + false, + ); }); }); diff --git a/frontend-modern/src/features/docs/__tests__/alertOffFAQ.test.ts b/frontend-modern/src/features/docs/__tests__/alertOffFAQ.test.ts index 75c24c7492..e481b4523e 100644 --- a/frontend-modern/src/features/docs/__tests__/alertOffFAQ.test.ts +++ b/frontend-modern/src/features/docs/__tests__/alertOffFAQ.test.ts @@ -5,7 +5,8 @@ import { describe, expect, it } from 'vitest'; import { renderDocMarkdown } from '../docMarkdown'; const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../../../..'); -const guide = (name: string): string => readFileSync(path.join(root, 'frontend-modern/public/docs', `${name}.md`), 'utf8'); +const guide = (name: string): string => + readFileSync(path.join(root, 'frontend-modern/public/docs', `${name}.md`), 'utf8'); const article = (name: string): HTMLElement => { const element = document.createElement('article'); element.innerHTML = renderDocMarkdown(guide(name), name); @@ -15,7 +16,11 @@ const section = (): HTMLElement => { const heading = article('FAQ').querySelector('#can-i-disable-alerts-for-specific-metrics'); expect(heading).not.toBeNull(); const element = document.createElement('section'); - for (let next = heading!.nextElementSibling; next && !/^H[1-6]$/.test(next.tagName); next = next.nextElementSibling) { + for ( + let next = heading!.nextElementSibling; + next && !/^H[1-6]$/.test(next.tagName); + next = next.nextElementSibling + ) { element.appendChild(next.cloneNode(true)); } return element; @@ -28,11 +33,14 @@ describe('shipped alert Off FAQ', () => { expect(text).toContain('not every default has a per-resource control'); expect(text).toContain('save, then reopen the editor'); expect(element.querySelectorAll('li')).toHaveLength(4); - for (const phrase of ['does not hide its readings, stop collection', - 'explicit threshold can remain active', 'Already queued notifications', + for (const phrase of [ + 'does not hide its readings, stop collection', + 'explicit threshold can remain active', + 'Already queued notifications', 'Patrol findings are separate from threshold alerts', 'when the corresponding alert default is Off', - 'Do not induce an alert, send another Test or retry the queue']) { + 'Do not induce an alert, send another Test or retry the queue', + ]) { expect(text).toContain(phrase); } expect(element.querySelector('pre')).toBeNull(); @@ -42,7 +50,10 @@ describe('shipped alert Off FAQ', () => { it('routes the two independent warning paths to real shipped headings', () => { const links = [...section().querySelectorAll('a')]; expect(links).toHaveLength(2); - expect(links.map((link) => link.textContent)).toEqual(['how Patrol works', 'ordinary notification checks']); + expect(links.map((link) => link.textContent)).toEqual([ + 'how Patrol works', + 'ordinary notification checks', + ]); for (const link of links) { const [name, fragment] = link.getAttribute('href')!.replace('/docs/', '').split('#'); expect(article(name).querySelector(`#${fragment}`)).not.toBeNull(); diff --git a/frontend-modern/src/utils/__tests__/docsLinks.test.ts b/frontend-modern/src/utils/__tests__/docsLinks.test.ts index 729b1c65b8..411703e438 100644 --- a/frontend-modern/src/utils/__tests__/docsLinks.test.ts +++ b/frontend-modern/src/utils/__tests__/docsLinks.test.ts @@ -657,9 +657,7 @@ describe('docsLinks', () => { /if !reportCallerMayUseAI\(ctx\) \{\s*return nil, nil, h\.resolveExistingFindings\(ctx\)\s*\}\s*return h\.narratorResolver\(ctx\)/, ); expect(resolver.match(/h\.narratorResolver\(/g)).toHaveLength(1); - expect(fleet).toMatch( - /_, fleetNarrator, findings := h\.resolveNarrator\(ctx\)/, - ); + expect(fleet).toMatch(/_, fleetNarrator, findings := h\.resolveNarrator\(ctx\)/); expect(fleet.match(/h\.resolveNarrator\(/g)).toHaveLength(1); }); diff --git a/scripts/installtests/provider_msp_startup_test.go b/scripts/installtests/provider_msp_startup_test.go index 47ece0c5c5..5a525d1aef 100644 --- a/scripts/installtests/provider_msp_startup_test.go +++ b/scripts/installtests/provider_msp_startup_test.go @@ -1,3 +1,5 @@ +//go:build linux + package installtests import ( @@ -265,12 +267,17 @@ func startupCommand(t *testing.T, env []string, dir, program string, args ...str defer cancel() cmd := exec.CommandContext(ctx, program, args...) cmd.Dir, cmd.Env = dir, env - output, err := cmd.CombinedOutput() - if err != nil { + var stdout, stderr bytes.Buffer + cmd.Stdout, cmd.Stderr = &stdout, &stderr + if err := cmd.Run(); err != nil { // Environment and licence contents are never diagnostic output. - t.Fatalf("%s command failed: %v\n%s", filepath.Base(program), err, output) + t.Fatalf("%s command failed: %v\n%s\n%s", filepath.Base(program), err, stdout.String(), stderr.String()) } - return string(output) + if stderr.Len() != 0 { + // Compose warnings are evidence, not container IDs, ports or CLI facts. + t.Logf("%s diagnostics: %s", filepath.Base(program), stderr.String()) + } + return stdout.String() } func startupRefusal(t *testing.T, env []string, dir, want, program string, args ...string) { @@ -384,3 +391,17 @@ func startupFacts(output string) map[string]string { } return facts } + +// Compose may warn on stderr about optional unset variables even when it +// succeeds. Never pass those diagnostics as the next docker exec identity. +func TestProviderMSPStartupCommandSeparatesDiagnostics(t *testing.T) { + for _, value := range []string{"synthetic-container-id", "127.0.0.1:8443", "provider_msp_bootstrap_ok=true"} { + t.Run(value, func(t *testing.T) { + output := startupCommand(t, []string{"PATH=" + os.Getenv("PATH")}, t.TempDir(), "sh", "-c", + `printf '%s\n' "$1"; printf 'optional variable warning\n' >&2`, "fixture", value) + if output != value+"\n" { + t.Fatalf("command facts include diagnostics: %q", output) + } + }) + } +}