@gfdelarue/testkit 0.4.4 → 0.4.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +108 -21
- package/bin/testkit-windows-owner-arm64.exe +0 -0
- package/bin/testkit-windows-owner-x64.exe +0 -0
- package/dist/app/bugs/categories.js +1 -1
- package/dist/app/bugs/draft.js +3 -3
- package/dist/app/bugs/facts.d.ts +3 -2
- package/dist/app/bugs/facts.js +2 -2
- package/dist/app/bugs/guide.js +1 -1
- package/dist/app/bugs/run-reading.js +1 -1
- package/dist/app/db-schema/prepare.js +1 -1
- package/dist/app/destroy/index.js +1 -1
- package/dist/app/doctor/index.js +1 -1
- package/dist/app/environments/config-driven.d.ts +5 -0
- package/dist/app/environments/config-driven.js +1 -0
- package/dist/app/environments/routing.d.ts +6 -0
- package/dist/app/environments/routing.js +1 -0
- package/dist/app/environments/supplied.d.ts +3 -0
- package/dist/app/environments/supplied.js +1 -0
- package/dist/app/file-selection.js +1 -1
- package/dist/app/lint/native.js +1 -1
- package/dist/app/lint/options.d.ts +0 -1
- package/dist/app/lint/options.js +1 -1
- package/dist/app/lint/shared.d.ts +0 -2
- package/dist/app/lint/shared.js +1 -1
- package/dist/app/package-inspection.js +1 -1
- package/dist/app/reports/files.d.ts +13 -0
- package/dist/app/reports/files.js +1 -0
- package/dist/app/reports/prepare.d.ts +20 -0
- package/dist/app/reports/prepare.js +2 -0
- package/dist/app/reports/publish.d.ts +21 -0
- package/dist/app/reports/publish.js +1 -0
- package/dist/app/run/engine-evidence-persistence.js +1 -1
- package/dist/app/run/engine-execution-cleanup.js +1 -1
- package/dist/app/run/engine-execution-failures.js +1 -1
- package/dist/app/run/engine-execution.d.ts +10 -8
- package/dist/app/run/engine-execution.js +1 -1
- package/dist/app/run/engine-orchestration.d.ts +15 -34
- package/dist/app/run/engine-orchestration.js +1 -1
- package/dist/app/run/environment.d.ts +33 -0
- package/dist/app/run/environment.js +1 -0
- package/dist/app/run/failure-details.js +1 -1
- package/dist/app/run/live-run.d.ts +3 -1
- package/dist/app/run/live-run.js +1 -1
- package/dist/app/run/orchestrator.d.ts +8 -1
- package/dist/app/run/orchestrator.js +1 -1
- package/dist/app/run/planning.d.ts +1 -2
- package/dist/app/run/planning.js +1 -1
- package/dist/app/run/results.d.ts +1 -1
- package/dist/app/run/results.js +1 -1
- package/dist/app/run/run-finalization.d.ts +6 -2
- package/dist/app/run/run-finalization.js +1 -1
- package/dist/app/run/runtime-fixtures.js +1 -1
- package/dist/app/run/runtime-task-types.d.ts +1 -1
- package/dist/app/run/scheduler/index.js +1 -1
- package/dist/app/run/scheduler/locks.d.ts +6 -0
- package/dist/app/run/scheduler/locks.js +1 -0
- package/dist/app/run/selection.d.ts +0 -1
- package/dist/app/run/selection.js +1 -1
- package/dist/app/run/setup/runtime-node-setup-protocol.js +1 -1
- package/dist/app/run/setup/runtime-node-setup-worker.js +1 -1
- package/dist/app/run/task-leases.d.ts +15 -0
- package/dist/app/run/task-leases.js +1 -0
- package/dist/app/run/worker-loop.d.ts +13 -4
- package/dist/app/run/worker-loop.js +1 -1
- package/dist/app/tracker/index.d.ts +6 -1
- package/dist/app/tracker/index.js +1 -1
- package/dist/app/typecheck/index.js +4 -4
- package/dist/cli/command-catalog.d.ts +19 -0
- package/dist/cli/command-catalog.js +1 -1
- package/dist/cli/command-registry.js +1 -1
- package/dist/cli/commands/report/prepare.d.ts +12 -0
- package/dist/cli/commands/report/prepare.js +5 -0
- package/dist/cli/commands/report/publish.d.ts +13 -0
- package/dist/cli/commands/report/publish.js +2 -0
- package/dist/cli/json-errors.js +1 -1
- package/dist/cli/operations/run/operation.d.ts +3 -1
- package/dist/cli/operations/run/operation.js +2 -2
- package/dist/cli/renderers/doctor/text.js +1 -1
- package/dist/cli/renderers/run/formatting.js +1 -1
- package/dist/config/authoring/index.d.ts +4 -13
- package/dist/config/authoring/index.js +1 -1
- package/dist/config/authoring/preset-env.js +1 -1
- package/dist/config/loader/config-loader.js +1 -1
- package/dist/config/loader/database.js +1 -1
- package/dist/config/loader/decode-execution.d.ts +0 -2
- package/dist/config/loader/decode-execution.js +1 -1
- package/dist/config/loader/discover-tests.js +1 -1
- package/dist/config/loader/discovery.d.ts +0 -1
- package/dist/config/loader/discovery.js +2 -2
- package/dist/config/loader/env.js +1 -1
- package/dist/config/loader/execution-model.d.ts +0 -1
- package/dist/config/loader/execution-model.js +1 -1
- package/dist/config/loader/file-metadata.d.ts +1 -1
- package/dist/config/loader/file-metadata.js +1 -1
- package/dist/config/loader/groups.d.ts +33 -0
- package/dist/config/loader/groups.js +1 -0
- package/dist/config/loader/load.d.ts +0 -2
- package/dist/config/loader/load.js +1 -1
- package/dist/config/loader/runtime-isolation.js +1 -1
- package/dist/config/loader/runtime.d.ts +0 -9
- package/dist/config/loader/runtime.js +1 -1
- package/dist/config/loader/skip-config.js +1 -1
- package/dist/config/loader/validation.d.ts +1 -2
- package/dist/config/loader/validation.js +1 -1
- package/dist/config/loader/values.d.ts +0 -1
- package/dist/config/loader/values.js +1 -1
- package/dist/database/activity-gate.js +1 -1
- package/dist/database/admin.js +2 -2
- package/dist/database/lifecycle-cleanup.d.ts +1 -0
- package/dist/database/lifecycle-cleanup.js +1 -1
- package/dist/database/local-postgres.js +1 -1
- package/dist/database/naming.js +1 -1
- package/dist/database/schema-operations.d.ts +1 -1
- package/dist/database/schema-operations.js +1 -1
- package/dist/database/schema-source.js +3 -3
- package/dist/database/source-url.d.ts +0 -1
- package/dist/database/source-url.js +2 -2
- package/dist/database/state.js +1 -1
- package/dist/database/steps.js +3 -3
- package/dist/domain/file-timeout.d.ts +0 -3
- package/dist/domain/file-timeout.js +1 -1
- package/dist/domain/operation-display.js +1 -1
- package/dist/domain/test-outcomes.d.ts +0 -1
- package/dist/domain/test-outcomes.js +1 -1
- package/dist/engines/adapters/evidence-identity.js +1 -1
- package/dist/engines/adapters/execution.d.ts +0 -1
- package/dist/engines/adapters/execution.js +1 -1
- package/dist/engines/api/capabilities.d.ts +0 -10
- package/dist/engines/api/capabilities.js +1 -1
- package/dist/engines/api/execution-scope.js +1 -1
- package/dist/engines/api/k6-runtime-input.js +1 -1
- package/dist/engines/bundle-cache.js +1 -1
- package/dist/engines/contracts/fixture-manifest.js +1 -1
- package/dist/engines/definitions.d.ts +1 -0
- package/dist/engines/definitions.js +1 -1
- package/dist/engines/installations/playwright-resolution-child.js +1 -1
- package/dist/engines/installations/playwright.js +1 -1
- package/dist/engines/k6/adapter/evidence.js +4 -4
- package/dist/engines/k6/adapter/executor.js +1 -1
- package/dist/engines/k6/adapter/native-summary.js +1 -1
- package/dist/engines/k6/runtime/checks.js +1 -1
- package/dist/engines/k6/runtime/dal-fixtures.js +1 -1
- package/dist/engines/k6/runtime/dal-suite.js +1 -1
- package/dist/engines/k6/runtime/http-suite-runtime.js +1 -1
- package/dist/engines/k6/runtime/http.js +1 -1
- package/dist/engines/k6/runtime/shared/db-normalization.js +1 -1
- package/dist/engines/k6/runtime/shared/fixture-engine.js +2 -2
- package/dist/engines/k6/runtime/shared/http-parsing.js +3 -3
- package/dist/engines/managed-module-resolution-policy-input.d.ts +0 -1
- package/dist/engines/managed-module-resolution-policy-input.js +1 -1
- package/dist/engines/managed-module-resolution-policy.js +1 -1
- package/dist/engines/node-vitest/adapter/adapter.js +1 -1
- package/dist/engines/node-vitest/adapter/evidence.js +2 -2
- package/dist/engines/node-vitest/adapter/workspace.js +22 -21
- package/dist/engines/node-vitest/runtime/contract/index.js +1 -1
- package/dist/engines/node-vitest/runtime/data/http.js +1 -1
- package/dist/engines/node-vitest/runtime/data/index.d.ts +2 -2
- package/dist/engines/node-vitest/runtime/data/index.js +1 -1
- package/dist/engines/node-vitest/runtime/data/report.js +5 -5
- package/dist/engines/node-vitest/runtime/data/scenario.d.ts +0 -1
- package/dist/engines/node-vitest/runtime/data/scenario.js +2 -2
- package/dist/engines/node-vitest/runtime/data/types.d.ts +1 -29
- package/dist/engines/node-vitest/runtime/runtime-input.js +1 -1
- package/dist/engines/playwright/adapter/adapter.js +1 -1
- package/dist/engines/playwright/adapter/config.d.ts +1 -1
- package/dist/engines/playwright/adapter/config.js +4 -4
- package/dist/engines/playwright/adapter/evidence-validation.d.ts +0 -3
- package/dist/engines/playwright/adapter/evidence-validation.js +1 -1
- package/dist/engines/playwright/adapter/evidence.js +1 -1
- package/dist/engines/playwright/runtime/config-file.js +1 -1
- package/dist/engines/playwright/runtime/config.js +1 -1
- package/dist/engines/playwright/runtime/runtime-input.d.ts +2 -2
- package/dist/engines/playwright/runtime/runtime-input.js +16 -12
- package/dist/environment/lifecycle-artifacts.js +1 -1
- package/dist/environment/lifecycle-controller.d.ts +1 -11
- package/dist/environment/lifecycle-controller.js +1 -1
- package/dist/environment/lifecycle.d.ts +14 -11
- package/dist/environment/lifecycle.js +1 -1
- package/dist/environment/planning-types.d.ts +0 -2
- package/dist/environment/ports.d.ts +10 -0
- package/dist/environment/ports.js +1 -0
- package/dist/environment/ready-environment.d.ts +2 -5
- package/dist/environment/ready-environment.js +1 -1
- package/dist/environment/run-environment.d.ts +1 -0
- package/dist/environment/run-environment.js +1 -1
- package/dist/environment/runtime-contexts.d.ts +11 -7
- package/dist/environment/runtime-contexts.js +1 -1
- package/dist/environment/runtime-manager.d.ts +1 -9
- package/dist/environment/runtime-manager.js +1 -1
- package/dist/environment/runtime-planning.js +1 -1
- package/dist/environment/runtime-preparation.js +1 -1
- package/dist/environment/runtime-service.d.ts +0 -6
- package/dist/environment/services.d.ts +11 -3
- package/dist/environment/services.js +3 -1
- package/dist/environment/state.d.ts +1 -4
- package/dist/environment/state.js +1 -1
- package/dist/environment/template.d.ts +6 -22
- package/dist/environment/template.js +1 -1
- package/dist/evidence/artifact-config.js +1 -1
- package/dist/evidence/artifact-validation.d.ts +1 -2
- package/dist/evidence/artifact-validation.js +1 -1
- package/dist/evidence/artifacts.js +2 -2
- package/dist/evidence/bundles.js +1 -1
- package/dist/evidence/environment-record.d.ts +22 -0
- package/dist/evidence/environment-record.js +1 -0
- package/dist/evidence/index.d.ts +1 -1
- package/dist/evidence/logs.js +2 -2
- package/dist/evidence/operation-timings.js +1 -1
- package/dist/evidence/read-fields.d.ts +0 -1
- package/dist/evidence/read-fields.js +1 -1
- package/dist/evidence/read-run.js +1 -1
- package/dist/evidence/reading.js +1 -1
- package/dist/evidence/reporting-types.d.ts +19 -0
- package/dist/evidence/reporting.d.ts +2 -2
- package/dist/evidence/reporting.js +1 -1
- package/dist/evidence/storage.d.ts +0 -2
- package/dist/evidence/storage.js +2 -2
- package/dist/evidence/timing.js +1 -1
- package/dist/kernel/error-serialization.d.ts +2 -0
- package/dist/kernel/error-serialization.js +1 -1
- package/dist/kernel/unknown-values.d.ts +5 -0
- package/dist/kernel/unknown-values.js +1 -1
- package/dist/process/environment.js +1 -1
- package/dist/process/execa-bundle.json +3 -3
- package/dist/process/files/json-file.d.ts +0 -1
- package/dist/process/files/json-file.js +1 -1
- package/dist/process/files/paths.d.ts +2 -0
- package/dist/process/files/paths.js +1 -1
- package/dist/process/host/metadata.js +1 -1
- package/dist/process/host/platform.js +1 -1
- package/dist/process/host-process.js +1 -1
- package/dist/process/lifecycle-fence.d.ts +1 -2
- package/dist/process/lifecycle-fence.js +1 -1
- package/dist/process/locks.js +2 -2
- package/dist/process/process-group.js +1 -1
- package/dist/process/process-identity.js +1 -1
- package/dist/process/process.js +30 -30
- package/dist/process/readiness.d.ts +7 -1
- package/dist/process/readiness.js +1 -1
- package/dist/process/recovery.js +1 -1
- package/dist/process/runs/manifest.js +1 -1
- package/dist/process/runs/ownership.js +1 -1
- package/dist/process/runs/product-state.js +1 -1
- package/dist/process/runs/stop.js +1 -1
- package/dist/process/stoppable-settlement.d.ts +26 -0
- package/dist/process/stoppable-settlement.js +1 -0
- package/dist/process/url-redaction.d.ts +1 -0
- package/dist/process/url-redaction.js +1 -0
- package/dist/process/windows-native.d.ts +6 -0
- package/dist/process/windows-native.js +1 -1
- package/dist/repo/ci-environment.js +1 -1
- package/dist/repo/fingerprint-policy.d.ts +0 -2
- package/dist/repo/fingerprint-policy.js +1 -1
- package/dist/repo/physical-tree.js +1 -1
- package/dist/repo/publish-directory.d.ts +20 -0
- package/dist/repo/publish-directory.js +3 -0
- package/dist/repo/state.js +4 -4
- package/dist/steps/build-config.d.ts +0 -1
- package/dist/steps/build-config.js +1 -1
- package/dist/steps/configured-steps.js +1 -1
- package/dist/steps/environment.d.ts +0 -2
- package/dist/steps/environment.js +1 -1
- package/dist/steps/index.d.ts +1 -1
- package/dist/steps/run.js +2 -2
- package/dist/toolchains/archive.js +1 -1
- package/dist/toolchains/index.js +3 -3
- package/dist/toolchains/node-provisioning.js +2 -2
- package/dist/toolchains/vitest/index.js +1 -1
- package/docs/bug-records.md +54 -52
- package/docs/engine-architecture.md +39 -11
- package/package.json +1 -1
- package/testkit-config-schema.md +43 -5
- package/dist/app/run/playwright-configuration.d.ts +0 -2
- package/dist/app/run/playwright-configuration.js +0 -1
- package/dist/environment/readiness.d.ts +0 -12
- package/dist/environment/readiness.js +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
import{resolveCommittedRunFinalization as
|
|
1
|
+
import{resolveCommittedRunFinalization as w}from"../../evidence/finalization.js";import{collectTestkitStorageInventory as x,writeRunArtifact as C}from"../../evidence/index.js";import{buildRunArtifact as N}from"../../evidence/reporting.js";import{summarizeDbBackend as T}from"./results.js";import{isUnknownRecord as V}from"../../kernel/unknown-values.js";export async function finalizeRunArtifacts({productDir:t,runId:e,results:r,startedAt:a,finishedAt:i,execution:A,workerCount:R,runtimeInstanceCount:S,runtimeStats:b,selection:n,archiveConfig:c=null,declaredEnvironmentKeys:p=[],metadata:F,environmentRecord:h,logRegistry:f,setupRegistry:y,operationTimingRegistry:z=null,reporter:k,runInterruptReason:d=null,runStatus:u=null,publishRunFinalization:m}){const s=N({productDir:t,runId:e,results:r,startedAt:a,finishedAt:i,execution:A,workerCount:R,runtimeInstanceCount:S,runtimeStats:b,typeValues:n.typeValues,suiteSelectors:n.suiteSelectors,fileNames:n.fileNames,serviceFilter:n.serviceFilter,planning:n.planning??null,archiveConfig:c,declaredEnvironmentKeys:p,metadata:F,summarizeDbBackend:T,environmentServices:h.listServices(),serviceLogs:f.listServiceLogs(),setupLogs:f.listSetupLogs(),setupOperations:y.listOperations(),operationTimings:z?.listOperations()??[],runInterruptReason:d,runStatus:u}),L=B(s.storage),O=x(t);s.storage={...L??{},testkit:O};const v=u==="failed"||r.some(o=>o.failed||o.infraFailed)?"failed":"passed",g=o=>{q(s,o);const l=j({archiveConfig:c,declaredEnvironmentKeys:p,productDir:t,runArtifact:s});return k?.runSummary?.(r,i-a,l.runArtifact.run.status==="interrupted"?"interrupted":l.runArtifact.run.status==="failed"?"failed":"passed"),l};return m?await m(v,g):g(w(v,{interrupted:u==="interrupted",interruptReason:u==="interrupted"?d:null}))}function j({archiveConfig:t,declaredEnvironmentKeys:e,productDir:r,runArtifact:a}){const i=C(r,a,{archiveConfig:t,declaredEnvironmentKeys:e});return{runArtifact:i,shouldFailArchiveCleanup:i.archive.cleanup?.status==="failed",shouldFailArchivePublication:i.archive.publication.status==="failed"&&i.archive.publication.required===!0}}function q(t,e){if(t.run.status=e.artifactStatus,e.artifactStatus!=="interrupted"){delete t.run.interruptReason;return}t.run.interruptReason=e.interruptReason??"run interrupted"}function B(t){return V(t)?t:null}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import x from"crypto";import
|
|
1
|
+
import x from"crypto";import c from"fs";import s from"path";import{posixPath as l}from"../../process/files/paths.js";const g=["__testkit__/fixtures"],P=new Map([[".csv","text/csv"],[".gif","image/gif"],[".jpg","image/jpeg"],[".jpeg","image/jpeg"],[".json","application/json"],[".pdf","application/pdf"],[".png","image/png"],[".txt","text/plain"],[".webp","image/webp"],[".xls","application/vnd.ms-excel"],[".xlsx","application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"],[".zip","application/zip"]]);export function buildRuntimeFixtureManifest(e,n){const i=d(e,n),t=[],o=new Set;for(const r of i)if(c.existsSync(r.absolutePath))for(const a of h(r.absolutePath)){const p=l(s.relative(e,a));if(o.has(p))continue;o.add(p);const f=l(s.relative(r.absolutePath,a)),m=c.statSync(a),u=c.readFileSync(a);t.push({aliases:[...new Set([f,s.basename(a),p])],absolutePath:a,contentType:y(a),filename:s.basename(a),path:p,root:r.relativePath,rootRelativePath:f,sha256:x.createHash("sha256").update(u).digest("hex"),sizeBytes:m.size})}return t.sort((r,a)=>r.path.localeCompare(a.path)),{version:1,testFile:l(n),entries:t}}function d(e,n){const i=[],t=s.dirname(n);i.push({relativePath:l(s.join(t,"__fixtures__")),absolutePath:s.join(e,t,"__fixtures__")});for(const o of g)i.push({relativePath:o,absolutePath:s.join(e,o)});return i}function h(e){const n=[],i=c.readdirSync(e,{withFileTypes:!0}).sort((t,o)=>t.name.localeCompare(o.name));for(const t of i){const o=s.join(e,t.name);t.isDirectory()?n.push(...h(o)):t.isFile()&&n.push(o)}return n}function y(e){return P.get(s.extname(e).toLowerCase())??"application/octet-stream"}
|
|
@@ -5,7 +5,7 @@ export interface RuntimeTask extends ArtifactTask {
|
|
|
5
5
|
file: string;
|
|
6
6
|
}
|
|
7
7
|
export interface RuntimeTaskReporter<Task extends RuntimeTask = RuntimeTask> {
|
|
8
|
-
taskStarted?(task: Readonly<Task>, targetConfig: Readonly<ReadyServiceConfig
|
|
8
|
+
taskStarted?(task: Readonly<Task>, targetConfig: Readonly<Pick<ReadyServiceConfig, "name">>): unknown;
|
|
9
9
|
}
|
|
10
10
|
export interface CompletedRuntimeTaskResult<FailureDetail = ReportingFailureDetail, CheckDetail = ReportingRecord> {
|
|
11
11
|
artifacts: PersistedArtifactReference[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{buildSchedulerTaskKey as f}from"./identity.js";import{estimateTask as y}from"./estimates.js";import{matchesSuiteSelectors as g}from"../../../domain/suite-selection.js";import{resolveRuntimeIsolationPolicy as h}from"../../../config/loader/index.js";export const SCHEDULER_POLICY="longest-estimated-duration-first",SCHEDULER_VERSION=2;const k=String;export function buildScheduledQueue(i,
|
|
1
|
+
import{buildSchedulerTaskKey as f}from"./identity.js";import{estimateTask as y}from"./estimates.js";import{matchesSuiteSelectors as g}from"../../../domain/suite-selection.js";import{resolveRuntimeIsolationPolicy as h}from"../../../config/loader/index.js";export const SCHEDULER_POLICY="longest-estimated-duration-first",SCHEDULER_VERSION=2;const k=String;export function buildScheduledQueue(i,o,{timings:e}={}){const t=new Map;for(const s of o)t.set(s.key,s);const r=[];let c=1;for(const s of i){if(s.skipped)continue;const a=t.get(s.assignedGraphKey);if(!a)throw new Error(`Assigned graph "${String(s.assignedGraphKey)}" not found for ${s.config.name}`);for(const n of s.suites)for(const l of n.files){const d={id:c,graphKey:a.key,targetName:s.config.name,serviceName:s.config.name,suiteKey:`${n.testType}:${n.name}`,suiteName:n.name,testType:n.testType,engineId:n.engineId,profileId:n.profileId,orderIndex:n.orderIndex,file:l,locks:I(s.config,n,l),runtimeIsolation:h(s.config,n,l),resourceCost:1},p=f(d),m={...d,timingKey:p},u=y({task:m,suite:n,timings:e});r.push({...m,estimatedDurationMs:u.durationMs,estimateSource:u.source,estimateConfidence:u.confidence,estimateRuns:u.runs,estimateKey:u.key}),c+=1}}return r.sort(compareScheduledTasks).map((s,a)=>({...s,schedulerRank:a+1,schedulerPolicy:SCHEDULER_POLICY}))}export function buildRunPlanningMetadata(i,o=[]){return{scheduler:{policy:SCHEDULER_POLICY,version:SCHEDULER_VERSION},resourceBudgets:v(o),tasks:(i||[]).map(e=>({rank:e.schedulerRank||null,id:e.id,service:e.serviceName,suite:e.suiteName,testType:e.testType,engineId:e.engineId,profileId:e.profileId,file:e.file,graphKey:e.graphKey,runtimeIsolation:e.runtimeIsolation,estimatedDurationMs:e.estimatedDurationMs,estimateSource:e.estimateSource,estimateConfidence:e.estimateConfidence,estimateRuns:e.estimateRuns||0,timingKey:e.timingKey}))}}function v(i){const o=new Map;for(const e of i||[])for(const t of e.resourceReservations||[]){const r=o.get(t.key)||{key:t.key,kind:t.kind,scope:t.scope,resourceName:t.resourceName,capacity:t.capacity,reserved:t.reserved,available:t.available,graphs:[]};r.capacity=Math.max(r.capacity||0,t.capacity||0),r.reserved=Math.max(r.reserved||0,t.reserved||0),r.available=Math.max(0,r.capacity-r.reserved),r.graphs.push({graphKey:e.key,services:t.services||[],perRuntime:t.perRuntime}),o.set(t.key,r)}return[...o.values()].sort((e,t)=>e.key.localeCompare(t.key))}export function compareScheduledTasks(i,o){return o.estimatedDurationMs-i.estimatedDurationMs||i.serviceName.localeCompare(o.serviceName)||i.suiteKey.localeCompare(o.suiteKey)||i.file.localeCompare(o.file)}function I(i,o,e){const t=new Set,r=i.testkit.requirements?.suites||[];for(const a of r)if(g(o.testType,o.name,[a.selector]))for(const n of a.locks||[])t.add(n);const c=k(e).split("\\").join("/"),s=i.testkit.fileMetadataByPath?.get(c)||null;for(const a of i.testkit.requirements?.fileLocksByPath?.get(c)||[])t.add(a);for(const a of s?.locks||[])t.add(a);return[...t].sort()}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function createTaskLocks(){const e=new Map;return{available(o){return o.every(r=>!e.has(r))},acquire(o,r){const n=[...new Set(o)],t=n.find(a=>e.has(a));if(t!==void 0)throw new Error(`Lock "${t}" is already held by ${String(e.get(t))}`);for(const a of n)e.set(a,r)},release(o,r){for(const n of o)e.get(n)===r&&e.delete(n)}}}
|
|
@@ -7,4 +7,3 @@ export interface SelectionSuiteCollectionOptions {
|
|
|
7
7
|
}
|
|
8
8
|
export type SelectionSuiteCollector<Config> = (config: Config, typeValues: readonly string[], suiteSelectors: readonly SuiteSelector[], fileNames: readonly string[], options: SelectionSuiteCollectionOptions) => readonly SelectionSuite[];
|
|
9
9
|
export declare function findUnmatchedRequestedFiles<Config>(configs: readonly Config[], typeValues: readonly string[], suiteSelectors: readonly SuiteSelector[], fileNames: readonly string[], collectSuites: SelectionSuiteCollector<Config>, normalizePathSeparators: (filePath: string) => string): string[];
|
|
10
|
-
export declare function isFullRunSelection(typeValues: readonly string[], suiteSelectors: readonly unknown[], fileNames: readonly string[], serviceFilter: null | string): boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export function findUnmatchedRequestedFiles(
|
|
1
|
+
export function findUnmatchedRequestedFiles(o,s,f,i,c,t){const n=new Set;for(const e of o){const u=c(e,s,f,[],{ignoreSkipRules:!0});for(const d of u)for(const r of d.files)n.add(t(r))}return[...new Set(i.map(t))].filter(e=>!n.has(e))}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const MAX_RUNTIME_NODE_SETUP_DATA_BYTES=
|
|
1
|
+
import{isUnknownRecord as u}from"../../../kernel/unknown-values.js";export const MAX_RUNTIME_NODE_SETUP_DATA_BYTES=1024*1024,MAX_RUNTIME_NODE_SETUP_SIDECARS=16;export function isRuntimeNodeSetupWorkerRequest(e){return u(e)&&typeof e.sessionId=="string"&&e.sessionId.length>0&&typeof e.modulePath=="string"}export function isRuntimeNodeSetupParentMessage(e){return R(e)&&(e.type==="cleanup"||e.type==="abort"&&u(e.error)&&N(e.kind))}export function readRuntimeNodeSetupWorkerMessage(e){if(!R(e))return null;switch(e.type){case"abort-acknowledged":break;case"cleanup-started":if(!Array.isArray(e.owners))return null;break;case"cleanup-progress":if(e.owner!=="consumer"&&e.owner!=="testkit"||!Number.isSafeInteger(e.remainingCallbacks))return null;break;case"ready":if(!("setupData"in e)||!Array.isArray(e.sidecars))return null;break;case"setup-error":if(!u(e.error)||!N(e.kind)||!Array.isArray(e.cleanupFailures))return null;break;case"aborted":case"cleanup-complete":if(!Array.isArray(e.cleanupFailures))return null;break;default:return null}return e}function N(e){return e==="cancelled"||e==="deadline"||e==="infra"||e==="product"}function R(e){return u(e)&&typeof e.type=="string"&&typeof e.sessionId=="string"&&e.sessionId.length>0}export function serializeRuntimeNodeSetupError(e){const r=g(e,"message"),o=g(e,"name"),i=g(e,"stack");return{message:typeof r=="string"?r:e===null||typeof e!="object"&&typeof e!="function"?String(e):"Runtime node setup failed",name:typeof o=="string"&&o?o:"Error",...typeof i=="string"?{stack:i}:{}}}export function serializeRuntimeNodeSetupCleanupFailures(e){return e.map(({error:r,owner:o})=>({error:serializeRuntimeNodeSetupError(r),owner:o}))}function g(e,r){const o=new Set;try{for(;u(e)&&!o.has(e);){o.add(e);const i=Object.getOwnPropertyDescriptor(e,r);if(i)return"value"in i?i.value:void 0;e=Object.getPrototypeOf(e)}}catch{}}export function normalizeRuntimeNodeSetupData(e){const r={},o=new Set,i=[{value:e===void 0?{}:e,path:"$",target:r,key:"data"}];let b=0;const a=(p,t)=>{throw new TypeError(`Runtime node setup data at ${p} must be plain JSON: ${t}`)},y=(p,t)=>{b+=p,b>MAX_RUNTIME_NODE_SETUP_DATA_BYTES&&a(t,`must encode to no more than ${MAX_RUNTIME_NODE_SETUP_DATA_BYTES.toString()} bytes`)};for(;i.length;){const p=i.pop();if(!p)break;if("leave"in p){o.delete(p.leave);continue}const{value:t,path:s,target:h,key:S}=p;if(t===null||typeof t=="string"||typeof t=="boolean"||typeof t=="number"&&Number.isFinite(t)){y(Buffer.byteLength(JSON.stringify(t)),s),Object.defineProperty(h,S,{value:t,enumerable:!0,writable:!0,configurable:!0});continue}typeof t!="object"&&a(s,`unsupported ${typeof t} value`),o.has(t)&&a(s,"circular reference");const c=Array.isArray(t),k=Object.getPrototypeOf(t);!c&&k!==Object.prototype&&k!==null&&a(s,"expected a plain object");const l=Reflect.ownKeys(t).filter(n=>!c||n!=="length");c&&l.length!==t.length&&a(s,"sparse array or extra array properties");const w=c?[]:{};Object.defineProperty(h,S,{value:w,enumerable:!0,writable:!0,configurable:!0}),y(2+Math.max(0,l.length-1),s),o.add(t),i.push({leave:t});const m=[];for(const n of l){typeof n!="string"&&a(s,"symbol key");const f=c?`${s}[${n}]`:/^[A-Za-z_$][\w$]*$/u.test(n)?`${s}.${n}`:`${s}[${JSON.stringify(n)}]`;c&&(!/^(?:0|[1-9]\d*)$/u.test(n)||Number(n)>=t.length)&&a(f,"extra array property");const d=Object.getOwnPropertyDescriptor(t,n);(!d||!("value"in d)||!d.enumerable)&&a(f,"expected an enumerable data property"),c||y(Buffer.byteLength(JSON.stringify(n))+1,f),m.push({value:d.value,path:f,target:w,key:n})}for(let n=m.length-1;n>=0;n-=1)i.push(m[n])}return r.data}export function normalizeRuntimeNodeSetupSidecarId(e){const r=typeof e=="string"&&e.trim()?e.trim():"http-capture";if(!/^[A-Za-z0-9._-]+$/u.test(r))throw new TypeError("Runtime node setup sidecar id must contain only letters, numbers, dot, underscore, or hyphen");return r}export function normalizeRuntimeNodeSetupSidecarPath(e){const r=typeof e=="string"&&e.trim()?e.trim():"/capture";if(!r.startsWith("/")||r.startsWith("//")||/[\\?#]/u.test(r))throw new TypeError("Runtime node setup sidecar path must be an absolute URL path");return r}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{classifyRuntimeNodeSetupFailure as X,createSetupFailure as w}from"./runtime-node-setup-errors.js";import Y from"node:http";import{pathToFileURL as G}from"node:url";import{parentPort as q,workerData as J}from"node:worker_threads";import{isRuntimeNodeSetupParentMessage as Q,isRuntimeNodeSetupWorkerRequest as W,MAX_RUNTIME_NODE_SETUP_SIDECARS as F,normalizeRuntimeNodeSetupData as K,normalizeRuntimeNodeSetupSidecarId as V,normalizeRuntimeNodeSetupSidecarPath as Z,serializeRuntimeNodeSetupError as $,serializeRuntimeNodeSetupCleanupFailures as ee}from"./runtime-node-setup-protocol.js";const
|
|
1
|
+
import{classifyRuntimeNodeSetupFailure as X,createSetupFailure as w}from"./runtime-node-setup-errors.js";import Y from"node:http";import{pathToFileURL as G}from"node:url";import{parentPort as q,workerData as J}from"node:worker_threads";import{isRuntimeNodeSetupParentMessage as Q,isRuntimeNodeSetupWorkerRequest as W,MAX_RUNTIME_NODE_SETUP_SIDECARS as F,normalizeRuntimeNodeSetupData as K,normalizeRuntimeNodeSetupSidecarId as V,normalizeRuntimeNodeSetupSidecarPath as Z,serializeRuntimeNodeSetupError as $,serializeRuntimeNodeSetupCleanupFailures as ee}from"./runtime-node-setup-protocol.js";import{isUnknownRecord as B}from"../../../kernel/unknown-values.js";import{toError as te}from"../../../kernel/error-serialization.js";const ne=1024*1024,re=100,oe=8*1024*1024,se=8*1024*1024,ae=1e3,ie=256,s=Te(J),O=Se(),p=new AbortController,C=[],A=new Set,D=[];let E=null,N=0,u="starting",_=null;O.on("message",e=>{ce(e)}),L(),ue();async function ue(){try{const t=await import(`${G(s.modulePath).href}?testkit=${encodeURIComponent(s.sessionId)}`);I();const n=me(t);if(!n)throw new TypeError(`Runtime node setup "${s.moduleRelativePath}" must export a setup function`);const r=await n(de()),o=ye(r);o&&C.push({callback:o,owner:"consumer"}),I();const c=K(he(r));b(),u="ready",d({sessionId:s.sessionId,setupData:c,sidecars:D.map(f=>({...f})),type:"ready"})}catch(e){const t=X(e),n=await P();u="cleaned",b(),d({cleanupFailures:n,error:$(e),kind:t,sessionId:s.sessionId,type:"setup-error"})}}async function ce(e){if(!Q(e)||e.sessionId!==s.sessionId){await x(w("infra","Runtime node setup worker received an invalid parent message"));return}if(e.type==="abort"){if(p.abort(w(e.kind,e.error.message)),d({sessionId:s.sessionId,type:"abort-acknowledged"}),u==="ready"){u="cleaning";const n=await P();u="cleaned",b(),d({cleanupFailures:n,sessionId:s.sessionId,type:"aborted"})}return}if(u==="cleaned"){d({cleanupFailures:[],sessionId:s.sessionId,type:"cleanup-complete"});return}if(u!=="ready"){await x(w("infra",`Runtime node setup cleanup is invalid during phase "${u}"`));return}u="cleaning";const t=await P();u="cleaned",b(),d({cleanupFailures:t,sessionId:s.sessionId,type:"cleanup-complete"})}async function x(e){p.abort(e);const t=await P();u="cleaned",b(),d({cleanupFailures:t,error:$(e),kind:"infra",sessionId:s.sessionId,type:"setup-error"})}function de(){return{deadlineMs:s.deadlineMs,env:{...s.runtimeEnv},leaseDir:s.leaseDir,productDir:s.productDir,serviceName:s.serviceName,sidecars:{httpCapture(e={}){if(I(),D.length+N>=F)return Promise.reject(new RangeError(`Runtime node setup may create at most ${F.toString()} sidecars`));N+=1;const t=le(e).then(n=>(C.push({callback:n.cleanup,owner:"testkit"}),D.push(n.handle),I(),n.handle)).finally(()=>{N-=1,A.delete(t)});return A.add(t),t}},signal:p.signal,testFile:s.testFile}}async function le(e){const t=[],n=new Set;let r=0;const o=V(e.id),c=Z(e.path),f=z(e.maxRequests,re,ae,"maxRequests"),m=z(e.maxBodyBytes,ne,oe,"maxBodyBytes"),T=be(e.status,204),l="/__testkit__/requests",S="/__testkit__/requests",a=Y.createServer((i,R)=>{if(i.method==="GET"&&i.url===l){g(R,200,{requests:t});return}if(i.method==="DELETE"&&i.url===S){t.splice(0),r=0,g(R,200,{ok:!0});return}fe(i,m).then(h=>{const U={bodyBytes:h.bodyBytes,bodyText:h.retainedBody.toString("utf8"),bodyTruncated:h.bodyBytes>h.retainedBody.length,headers:i.headers,method:i.method??null,path:i.url??null,receivedAt:new Date().toISOString()};for(r+=Buffer.byteLength(U.bodyText),t.push(U);t.length>f||r>se;){const M=t.shift();if(!M)break;r-=Buffer.byteLength(M.bodyText)}g(R,T,{ok:!0})}).catch(h=>{g(R,500,{error:Re(h)})})});a.maxConnections=ie,a.requestTimeout=3e4,a.headersTimeout=3e4,a.keepAliveTimeout=1e3,a.on("connection",i=>{n.add(i),i.once("close",()=>n.delete(i))}),await pe(a);const y=a.address();if(y===null||typeof y=="string")throw await H(a,n),new Error("Runtime node HTTP capture did not bind a TCP port");const k=`http://127.0.0.1:${y.port.toString()}`,j={id:o,requestsUrl:`${k}${l}`,resetUrl:`${k}${S}`,type:"http-capture",url:`${k}${c}`};return{cleanup:()=>H(a,n),handle:j}}function pe(e){return new Promise((t,n)=>{const r=o=>{n(o)};e.once("error",r),e.listen(0,"127.0.0.1",()=>{e.off("error",r),t()})})}function H(e,t){if(!e.listening){for(const n of t)n.destroy();return Promise.resolve()}return new Promise((n,r)=>{e.close(o=>{o?r(o):n()}),e.closeIdleConnections(),e.closeAllConnections();for(const o of t)o.destroy()})}function fe(e,t){return new Promise((n,r)=>{const o=[];let c=0,f=0,m=!1;const T=l=>{m||(m=!0,r(te(l)))};e.on("data",l=>{if(m)return;if(typeof l!="string"&&!(l instanceof Uint8Array)){T(new TypeError("HTTP capture received an unsupported body chunk"));return}const S=Buffer.from(l);c=Math.min(Number.MAX_SAFE_INTEGER,c+S.length);const a=t-f;if(a>0){const y=S.subarray(0,a);o.push(y),f+=y.length}}),e.once("aborted",()=>{T(new Error("HTTP request was aborted"))}),e.once("error",T),e.once("end",()=>{m||(m=!0,n({bodyBytes:c,retainedBody:Buffer.concat(o,f)}))})})}function g(e,t,n){e.destroyed||e.writableEnded||(e.writeHead(t,{"content-type":"application/json"}),e.end(JSON.stringify(n)))}function me(e){if(!B(e))return null;const t=e.setup??e.default;return typeof t=="function"?t:null}function ye(e){if(!B(e))return null;const t=v(e,"cleanup");return typeof t=="function"?t:null}function he(e){return B(e)&&("data"in e||"cleanup"in e)?Object.hasOwn(e,"data")?v(e,"data"):{}:e??{}}function v(e,t){let n=e;for(;n!==null;){const r=Object.getOwnPropertyDescriptor(n,t);if(r){if(!("value"in r))throw new TypeError(`Runtime node setup data at $.${t} must be an enumerable data property`);return r.value}n=Object.getPrototypeOf(n)}}function P(){return E!==null||(E=(async()=>{await Promise.allSettled([...A]);const e=[],t=C.splice(0).reverse();d({owners:t.map(({owner:n})=>n),sessionId:s.sessionId,type:"cleanup-started"});for(const[n,{callback:r,owner:o}]of t.entries()){d({owner:o,remainingCallbacks:t.length-n,sessionId:s.sessionId,type:"cleanup-progress"});try{await r()}catch(c){e.push({error:c,owner:o})}}return ee(e)})()),E}function L(){const e=s.deadlineMs-Date.now();if(e<=0){p.abort(we());return}_=setTimeout(L,Math.min(e,2147483647))}function b(){_!==null&&clearTimeout(_),_=null}function we(){return w("deadline",`Runtime node setup exceeded its deadline ${s.deadlineMs.toString()}`)}function I(){if(p.signal.aborted)throw p.signal.reason instanceof Error?p.signal.reason:new Error(String(p.signal.reason))}function d(e){O.postMessage(e)}function Te(e){if(!W(e))throw w("infra","Runtime node setup worker received invalid worker data");return e}function Se(){if(q===null)throw w("infra","Runtime node setup worker requires a parent port");return q}function z(e,t,n,r){if(e===void 0)return t;const o=Number(e);if(!Number.isInteger(o)||o<=0||o>n)throw new RangeError(`Runtime node HTTP capture ${r} must be an integer between 1 and ${n.toString()}`);return o}function be(e,t){if(e===void 0)return t;const n=Number(e);return Number.isInteger(n)&&n>=100&&n<=599?n:t}function Re(e){return e instanceof Error?e.message:String(e)}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { EnvironmentLease } from "./environment.js";
|
|
2
|
+
export interface TaskLease extends EnvironmentLease {
|
|
3
|
+
readonly engineCacheDirectory: string;
|
|
4
|
+
readonly engineWorkspaceDirectory: string;
|
|
5
|
+
}
|
|
6
|
+
export interface TaskLeases {
|
|
7
|
+
allocate(task: Readonly<{
|
|
8
|
+
id: number;
|
|
9
|
+
}>): TaskLease;
|
|
10
|
+
close(): void;
|
|
11
|
+
create(lease: Readonly<TaskLease>): void;
|
|
12
|
+
remove(lease: Readonly<TaskLease>): void;
|
|
13
|
+
}
|
|
14
|
+
export declare function taskExecutionRoot(productDir: string): string;
|
|
15
|
+
export declare function createTaskLeases(productDir: string): TaskLeases;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import t from"node:fs";import r from"node:path";export function taskExecutionRoot(i){return r.join(i,".testkit","_execution")}export function createTaskLeases(i){const n=taskExecutionRoot(i),u=r.join(n,"leases"),c=r.join(n,"node_modules","testkit-engine"),a=r.join(c,"cache");t.rmSync(n,{recursive:!0,force:!0});let y=1;return{allocate(e){const o=`lease-${e.id.toString()}-${y.toString()}`;return y+=1,{id:o,directory:r.join(u,o),engineCacheDirectory:a,engineWorkspaceDirectory:r.join(c,"leases",o)}},create(e){t.mkdirSync(e.directory,{recursive:!0})},remove(e){t.rmSync(e.engineWorkspaceDirectory,{recursive:!0,force:!0}),t.rmSync(e.directory,{recursive:!0,force:!0})},close(){t.rmSync(a,{recursive:!0,force:!0});for(const e of[r.join(c,"leases"),c,r.dirname(c),u,n])try{t.rmdirSync(e)}catch(o){const s=o.code;if(s!=="ENOENT"&&s!=="ENOTEMPTY"&&s!=="EEXIST")throw o}}}}
|
|
@@ -1,14 +1,23 @@
|
|
|
1
|
-
import { type EngineExecutionManager } from "./engine-execution.js";
|
|
1
|
+
import { type EngineExecutionLifecycle, type EngineExecutionManager } from "./engine-execution.js";
|
|
2
|
+
import type { ExecutionRequest } from "./engine-orchestration.js";
|
|
3
|
+
import type { EnvironmentEntry } from "./environment.js";
|
|
4
|
+
import type { TaskLeases } from "./task-leases.js";
|
|
5
|
+
import type { TaskLocks } from "./scheduler/locks.js";
|
|
2
6
|
import type { TaskFailureDetail } from "./failure-details.js";
|
|
3
7
|
import type { TestOutcomeStatus } from "../../domain/test-outcomes.js";
|
|
4
|
-
import type {
|
|
5
|
-
import { type claimNextTask as claimScheduledTask } from "./planning.js";
|
|
8
|
+
import type { claimNextTask as claimScheduledTask } from "./planning.js";
|
|
6
9
|
import type { recordGraphError, recordTaskOutcome, ServiceTrackerMap } from "./results.js";
|
|
7
10
|
import { type TimingObservation } from "./scheduler/observations.js";
|
|
8
11
|
import type { ScheduledTask } from "./scheduler/index.js";
|
|
9
12
|
import type { RuntimeTaskReporter } from "./runtime-task-types.js";
|
|
10
13
|
import { type TaskBatch } from "./task-batches.js";
|
|
11
14
|
export type WorkerExecutableTask = ScheduledTask | TaskBatch<ScheduledTask>;
|
|
15
|
+
export interface WorkerRun extends EngineExecutionLifecycle {
|
|
16
|
+
readonly environments: EnvironmentEntry;
|
|
17
|
+
readonly leases: TaskLeases;
|
|
18
|
+
readonly locks: TaskLocks;
|
|
19
|
+
readonly request: ExecutionRequest;
|
|
20
|
+
}
|
|
12
21
|
export interface WorkerOutcomeTaskIdentity {
|
|
13
22
|
file: string;
|
|
14
23
|
id: number | string;
|
|
@@ -53,5 +62,5 @@ export type WorkerTaskClaimer = typeof claimScheduledTask<ScheduledTask>;
|
|
|
53
62
|
export type WorkerTaskOutcomeRecorder = typeof recordTaskOutcome;
|
|
54
63
|
export type WorkerGraphErrorRecorder = typeof recordGraphError;
|
|
55
64
|
export declare function createWorker(workerId: number, productDir: string): WorkerState;
|
|
56
|
-
export declare function runWorker(worker: WorkerState, queue: ScheduledTask[], run:
|
|
65
|
+
export declare function runWorker(worker: WorkerState, queue: ScheduledTask[], run: WorkerRun, engineManager: EngineExecutionManager, trackers: ServiceTrackerMap, timingUpdates: TimingObservation[], claimNextTask: WorkerTaskClaimer, recordTaskOutcome: WorkerTaskOutcomeRecorder, recordGraphError: WorkerGraphErrorRecorder, reporter?: null | WorkerReporter): Promise<WorkerResult>;
|
|
57
66
|
export declare function resolveWorkerOutcomeMappings<Task extends WorkerOutcomeTaskIdentity, Outcome extends WorkerOutcomeWithTaskIdentity>(tasks: readonly Task[], outcomes: readonly Outcome[]): Array<ResolvedWorkerOutcome<Task, Outcome>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{EngineExecutionBridgeError as b,executeEngineTask as
|
|
1
|
+
import{EngineExecutionBridgeError as b,executeEngineTask as N}from"./engine-execution.js";import{formatError as P,toError as C}from"../../kernel/error-serialization.js";import{formatEngineInvocationStack as j}from"./engine-failure-stack.js";import{DEFAULT_ENGINE_REPORTER_CALLBACK_TIMEOUT_MS as U,invokeBoundedReporterCallback as z}from"./engine-execution-reporting.js";import{drainRuntimeFailedTasks as J}from"./runtime-failures.js";import{buildTimingObservation as H}from"./scheduler/observations.js";import{buildTaskBatch as Q,claimCompatibleTaskBatch as V,expandTaskResultOutcomes as X}from"./task-batches.js";export function createWorker(e,i){return{workerId:e,productDir:i,currentGraphKey:null,graphSwitches:0,taskCount:0}}export async function runWorker(e,i,t,r,a,c,l,h,d,m=null){const y=Date.now(),k=[],x=[];try{for(;!t.isStopRequested();){const u=l(i,e.currentGraphKey,n=>t.locks.available(n.locks)&&t.environments.canAcquire(n));if(!u){const n=J(i,t.environments);if(n.length>0){const o=Date.now(),s=new Set;for(const g of n)h(a,g.task,{status:"not_run",reason:g.reason,durationMs:0,startedAt:o,finishedAt:o,error:null,infraFailed:g.infraFailed,blockedByRuntime:g.blockedByRuntime,failureDetails:g.failureDetails}),s.has(g.graph.key)||(d(a,g.graph,g.message,o,{classification:"infra"}),s.add(g.graph.key));e.taskCount+=n.length;continue}if(i.length===0)break;await ae(10);continue}const w=t.leases.allocate(u);t.locks.acquire(u.locks,w.id);let v=null,T=[u],I=u,S=!1,B=null,$="acquire",R=[],F=null;try{T=V(i,u),I=Q(T)??u,e.currentGraphKey!==null&&e.currentGraphKey!==u.graphKey&&(e.graphSwitches+=1),e.currentGraphKey=u.graphKey,v=await t.environments.acquire(I,w),t.leases.create(w),$="execute";const n=await N(r,{environment:v.environment,lease:w,nativeConfigDirectory:v.nativeConfigDirectory,request:t.request,task:I},t,m);$="mapping";const o=X(n);R=resolveWorkerOutcomeMappings(T,o),R.some(({outcome:s})=>s.infraFailed&&s.blockedByRuntime)&&(S=!0)}catch(n){const o=$==="acquire"?ie(n,t):te(n);B={disposition:o,error:n},S=v!==null&&o.runtimeImpact!=="none"}finally{F=await Z(t.leases,v,w,S),t.locks.release(u.locks,w.id)}const f=[];let E=null;const D=()=>{if(E!==null)return E;try{E=ne(t.environments,u)}catch(n){f.push(M("Runtime failure graph resolution failed",n)),E=W(u)}return E};if(B===null){for(const{outcome:o,task:s}of R)await A({operationErrors:f,outcome:o,recordTaskOutcome:h,recordTiming:!0,reporter:m,reportTaskFinished:!1,task:s,timingUpdates:c,trackers:a});e.taskCount+=R.length;const n=R.filter(({outcome:o})=>o.infraFailed&&o.blockedByRuntime);if(n.length>0){const o=n.map(({outcome:s})=>s.reason??s.error??"Runtime readiness failed before engine execution").join("; ");k.push(o),await O(f,"Runtime error reporter failed",()=>m?.runtimeError?.(u,o)),await p(f,"Runtime graph failure recording failed",()=>{t.environments.markGraphFailed(u.graphKey,o)}),await p(f,"Result graph error recording failed",()=>{d(a,D(),o,Date.now(),{classification:"infra"})})}}else{const{disposition:n,error:o}=B,s=P(o),g=Y(T,o);n.status!=="cancelled"&&k.push(s),n.notifyRuntimeError&&await O(f,"Runtime error reporter failed",()=>m?.runtimeError?.(g,s)),n.status!=="cancelled"&&(n.runtimeImpact==="fail-graph"&&await p(f,"Runtime graph failure recording failed",()=>{t.environments.markGraphFailed(u.graphKey,s)}),await p(f,"Result graph error recording failed",()=>{d(a,D(),s,Date.now(),{classification:n.classification??"product"})}));const q=Date.now();for(const K of T){const _=o instanceof b&&o.completedOutcomesByTaskId.has(String(K.id)),L=ee(K,I,o,n,s,q);await A({operationErrors:f,outcome:L,recordTaskOutcome:h,recordTiming:_,reporter:m,reportTaskFinished:!_,task:K,timingUpdates:c,trackers:a})}e.taskCount+=T.length}if(F!==null){const n=F.message;k.push(n),await O(f,"Runtime error reporter failed",()=>m?.runtimeError?.(u,n)),F.recovered||await p(f,"Runtime graph failure recording failed",()=>{t.environments.markGraphFailed(u.graphKey,n)}),await p(f,"Result graph error recording failed",()=>{d(a,D(),n,Date.now(),{classification:"infra"})})}if(f.length>0){const n=[...f];for(const s of n)k.push(s.message);const o=n.map(s=>s.message).join("; ");await p(f,"Result graph error recording failed while reporting a Testkit operation failure",()=>{d(a,D(),o,Date.now(),{classification:"infra"})});for(const s of f.slice(n.length))k.push(s.message);f.length>n.length&&x.push(...n.map(s=>s.message),...f.slice(n.length).map(s=>s.message))}}}finally{e.currentGraphKey=null}return{workerId:e.workerId,failed:k.length>0,durationMs:Date.now()-y,taskCount:e.taskCount,graphSwitches:e.graphSwitches,errors:k,unpersistedErrors:x}}async function A({operationErrors:e,outcome:i,recordTaskOutcome:t,recordTiming:r,reporter:a,reportTaskFinished:c,task:l,timingUpdates:h,trackers:d}){await p(e,`Task outcome recording failed for ${l.file}`,()=>{t(d,l,i)}),c&&await O(e,`Task-finished reporter failed for ${l.file}`,()=>a?.taskFinished?.(l,i)),r&&await p(e,`Task timing recording failed for ${l.file}`,()=>{const m=H(l,i);m&&h.push(m)})}async function p(e,i,t){try{await t()}catch(r){e.push(M(i,r))}}async function O(e,i,t){await p(e,i,()=>z(i,t,{timeoutMs:U}))}function M(e,i){const t=C(i);return new Error(`${e}: ${t.message}`,{cause:t})}function Y(e,i){const t=e[0];if(!(i instanceof b))return t;if(i.failingTaskId!==null){const r=e.find(a=>String(a.id)===i.failingTaskId);if(r!==void 0)return r}return e.find(r=>!i.completedOutcomesByTaskId.has(String(r.id)))??t}function W(e){return{key:e.graphKey,targetNames:[e.targetName]}}async function Z(e,i,t,r){const a=async c=>{await i?.release({invalidate:c}),e.remove(t)};try{return await a(r),null}catch(c){const l=C(c);try{return await a(!0),{message:`Runtime lease release failed but recovered on idempotent retry: ${l.message}`,recovered:!0}}catch(h){const d=C(h);return{message:`Runtime lease release failed: ${l.message}; idempotent retry also failed: ${d.message}`,recovered:!1}}}}function ee(e,i,t,r,a,c){const l=j(t),h={kind:"engine-invocation",key:"engine_invocation_failed",title:"Engine invocation failed",count:1,...r.classification===void 0?{}:{classification:r.classification},message:a,...l===void 0?{}:{stack:l}},d=t instanceof b?t.completedOutcomesByTaskId.get(String(e.id)):void 0;if(d!==void 0)return d;const m=t instanceof b?t.artifactReferencesByTaskId.get(String(e.id))??[]:[];return{task:e,status:r.status,error:r.status==="failed"?a:null,reason:r.status==="failed"?null:a,infraFailed:r.classification==="infra",blockedByRuntime:r.status==="not_run",durationMs:0,startedAt:c,finishedAt:c,artifacts:[...m],failureDetails:r.status==="cancelled"?[]:[h],checkDetails:[],..."batchTasks"in i?{runtimeBatch:{id:i.batchId,index:i.batchTasks.findIndex(y=>y.id===e.id&&y.file===e.file),fileCount:i.batchTasks.length,files:i.batchTasks.map(y=>y.file)}}:{}}}function te(e){return e instanceof b?e.disposition:{classification:"infra",notifyRuntimeError:!0,runtimeImpact:"none",status:"not_run"}}function ie(e,i){return i.signal.aborted&&e===i.signal.reason?{classification:void 0,notifyRuntimeError:!1,runtimeImpact:"none",status:"cancelled"}:{classification:"infra",notifyRuntimeError:!0,runtimeImpact:"fail-graph",status:"not_run"}}export function resolveWorkerOutcomeMappings(e,i){const t=new Map;for(const a of e){const c=G(a);if(t.has(c))throw new Error(`Worker claimed duplicate task ${a.id.toString()} (${a.file})`);t.set(c,a)}const r=new Map;for(const a of i){const c=G(a.task),l=t.get(c);if(!l)throw new Error(`Task runner returned an outcome for unclaimed task ${a.task.id.toString()} (${a.task.file})`);if(r.has(c))throw new Error(`Task runner returned duplicate outcomes for claimed task ${l.id.toString()} (${l.file})`);r.set(c,{outcome:a,task:l})}for(const a of e)if(!r.has(G(a)))throw new Error(`Task runner did not return an outcome for claimed task ${a.id.toString()} (${a.file})`);return[...r.values()]}function G(e){return JSON.stringify([typeof e.id,e.id,e.file])}function ne(e,i){return e.getGraph(i.graphKey)??W(i)}function ae(e){return new Promise(i=>setTimeout(i,e))}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { Tracker } from "../../tracker/types.js";
|
|
1
|
+
import type { Tracker, TrackerTarget } from "../../tracker/types.js";
|
|
2
|
+
import { type TrackerRepositoryLocation } from "./remote.js";
|
|
2
3
|
export { TrackerTargetError } from "./remote.js";
|
|
3
4
|
export interface TrackerResolutionRequest {
|
|
4
5
|
readonly env: Readonly<NodeJS.ProcessEnv>;
|
|
@@ -6,3 +7,7 @@ export interface TrackerResolutionRequest {
|
|
|
6
7
|
readonly repository?: string | undefined;
|
|
7
8
|
}
|
|
8
9
|
export declare function resolveTracker(request: TrackerResolutionRequest): Promise<Tracker>;
|
|
10
|
+
export declare function resolveRepositoryConnection(request: TrackerResolutionRequest): Promise<{
|
|
11
|
+
location: TrackerRepositoryLocation;
|
|
12
|
+
target: TrackerTarget;
|
|
13
|
+
}>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{runManagedGitCommand as l}from"../../repo/managed-git.js";import{readGiteaVersion as p}from"../../tracker/gitea.js";import{createTracker as u}from"../../tracker/index.js";import{displayRepositoryReference as m,githubActionsApiUrl as h,parseTrackerRemote as
|
|
1
|
+
import{runManagedGitCommand as l}from"../../repo/managed-git.js";import{readGiteaVersion as p}from"../../tracker/gitea.js";import{createTracker as u}from"../../tracker/index.js";import{displayRepositoryReference as m,githubActionsApiUrl as h,parseTrackerRemote as g,parseTrackerRepositoryOverride as c,TrackerTargetError as n}from"./remote.js";import{readGitCredentialToken as d,trackerTokenFromEnvironment as f,trackerTokenVariables as w}from"./token.js";export{TrackerTargetError}from"./remote.js";const v="github.com",T="https://api.github.com";export async function resolveTracker(r){return u((await resolveRepositoryConnection(r)).target)}export async function resolveRepositoryConnection(r){const t=await k(r),{actionsRun:o,apiUrl:e,provider:i}=await R(t,r.env),a=w(i,o),s=f(a,r.env)??await d(r.productDir,t,r.env);if(s===null)throw new n(`Testkit found no token for ${t.origin}. Set ${a.join(" or ")}, or give Git a credential helper that holds one for that host.`);return{location:t,target:{apiUrl:e,name:t.name,owner:t.owner,provider:i,token:s}}}async function k(r){const t=r.repository?.trim()??"";if(/^https?:\/\//iu.test(t))return c(t,null);const o=await y(r),e=o===null?null:g(o);if(t.length>0)return c(t,e);if(e!==null)return e;throw new n(o===null?`${r.productDir} has no origin remote, so Testkit cannot tell which repository to report to. Add the remote, or name the repository as a URL such as https://host/owner/name.`:`Testkit cannot read a host, owner, and repository name from the origin remote ${JSON.stringify(m(o))}. Name the repository as a URL such as https://host/owner/name.`)}async function y(r){const t=await l(r.productDir,["config","--get","remote.origin.url"],{env:r.env,repositoryConfigOnly:!0}),o=t.stdout.trim();return t.exitCode===0&&o.length>0?o:null}async function R(r,t){if(new URL(r.origin).host===v)return{actionsRun:!1,apiUrl:T,provider:"github"};const o=h(r.origin,t);if(await p(r.origin)!==null)return{actionsRun:o!==null,apiUrl:o??`${r.origin}/api/v1`,provider:"gitea"};if(o!==null)return{actionsRun:!0,apiUrl:o,provider:"github"};throw new n(`${r.origin} does not answer as GitHub or Gitea. Testkit supports GitHub and Gitea.`)}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{attachErrorDiagnostics as
|
|
2
|
-
`),s}function Y({productDir:e,cwd:n,engineId:t,extendsPath:s,files:o,outputDir:r,scopeName:a}){const c=i.join(r,`${t}.${te(a)}.tsconfig.json`),
|
|
3
|
-
`),c}function W(e,n){const t=new Map,s=new Map;for(const o of n){const r=i.resolve(e,o.path),a=[o.engineId,r].join("\0");s.set(a,{...o,path:r})}for(const o of s.values()){const r=o.path,a=o.engineId==="playwright"?
|
|
4
|
-
`)}`)},s=n.getParsedCommandLineOfConfigFile(e,{},{...n.sys,onUnRecoverableConfigFileDiagnostic:t});for(const o of s?.errors??[])o.code!==18002&&o.code!==18003&&t(o);return s?.fileNames??[]}function Q(e){switch(e){case"k6":return["k6","xk6-sql"];case"node-vitest":case"playwright":return["node"]}}function Z(e){return e==="playwright"?["ES2022","DOM","DOM.Iterable"]:["ES2022"]}function
|
|
1
|
+
import{attachErrorDiagnostics as S}from"../../kernel/error-serialization.js";import{isPathWithin as T}from"../../process/files/paths.js";import d from"fs";import i from"path";import{createHash as j}from"crypto";import{createRequire as _}from"module";import{fileURLToPath as C}from"url";import{NODE_VITEST_PACKAGE_REQUIREMENTS as O}from"../../engines/installations/package-requirements.js";import{PLAYWRIGHT_PACKAGE_FAMILY_REQUIREMENTS as $}from"../../engines/installations/package-requirements.js";import{hasDiscoveryErrors as R,buildDiscoveryErrorFromDiagnostics as A,detectNextApp as M,loadDiscoveredConfigContext as I,resolveServiceCwd as F}from"../../config/loader/index.js";import{K6_POSTGRES_DRIVER_SPECIFIER as N,K6_POSTGRES_DRIVER_TYPE_FILE as D,K6_RUNTIME_GLOBAL_TYPE_FILE as L}from"../../engines/k6/authoring-type-paths.js";import{managedHostProcessInfrastructureError as G,runManagedHostProcess as U}from"../../process/index.js";import{CONFIGURED_STEP_TESTKIT_MODULE_DIST_PATHS as K}from"../../steps/configured-step-testkit-modules.js";import{findPlaywrightConfigFile as z}from"../../engines/playwright/runtime/config-file.js";import{isUnknownRecord as k}from"../../kernel/unknown-values.js";const J=600*1e3,h=i.resolve(i.dirname(C(import.meta.url)),"..","..",".."),x=_(i.join(h,"package.json")),E=()=>x("typescript"),q={...B(["@gfdelarue/testkit","@gfdelarue/testkit/config","@gfdelarue/testkit/config/build-assets","@gfdelarue/testkit/config/next-runtime-tsconfig","@gfdelarue/testkit/config/database-steps","@gfdelarue/testkit/drizzle","@gfdelarue/testkit/env","@gfdelarue/testkit/tooling/vitest"]),"@gfdelarue/testkit/k6":["engines","k6","runtime","index.d.ts"],"@gfdelarue/testkit/playwright":["engines","playwright","runtime","index.d.ts"],"@gfdelarue/testkit/node-vitest/data":["engines","node-vitest","runtime","data","index.d.ts"],"@gfdelarue/testkit/node-vitest/contract":["engines","node-vitest","runtime","contract","index.d.ts"],"@gfdelarue/testkit/workspace":["app","inspect","index.d.ts"]};function B(e){return Object.fromEntries(e.map(n=>{const t=K[n],s=t[t.length-1];return[n,[...t.slice(0,-1),`${s}.d.ts`]]}))}export class TestkitTypecheckError extends Error{result;results;constructor(n,t,s){super(`testkit typecheck failed for ${n}`),this.result=t,this.results=s,S(this,{result:t,results:s})}}export async function runTestkitTypecheck(e={}){b(e.timeoutMs);const{context:n,discovery:t}=await I({dir:e.dir});return runTestkitTypecheckForContext(n,t,e)}export async function runTestkitTypecheckForContext(e,n,t={}){const s=b(t.timeoutMs);if(R(e.discovery.diagnostics))throw A(e.discovery.diagnostics);const o=e.productDir,r=i.join(o,".testkit","_typecheck");d.mkdirSync(r,{recursive:!0});const a=[];if(e.configFile){const p=H({productDir:o,configFile:e.configFile,outputDir:r});a.push({label:"config",cwd:o,tsconfigPath:p})}const c=n.files.map(p=>({engineId:p.engineId,path:p.path})),u=new Set(n.files.filter(p=>p.engineId==="playwright").map(p=>p.service));for(const p of e.configs){if(!u.has(p.name))continue;const y=z(F(o,p.testkit.local?.cwd));y&&c.push({engineId:"playwright",path:y})}const f=W(o,c);for(const p of f){const y=Y({...p,outputDir:r,productDir:o});a.push({label:X(p.engineId,p.scopeName),cwd:p.cwd,tsconfigPath:y})}const g=[];for(const p of a){const y=await ne(p,s,t.signal);if(g.push(y),!y.ok)throw new TestkitTypecheckError(p.label,y,g)}return{ok:!0,productDir:o,results:g}}function H({productDir:e,configFile:n,outputDir:t}){const s=i.join(t,"config.tsconfig.json"),o=P(e),r={...o?{extends:l(s,o)}:{},compilerOptions:buildTypecheckCompilerOptions({tsconfigPath:s,extendsPath:o,libs:["ES2022"],rootDir:l(s,e),types:["node"]}),files:[l(s,n)],include:[],exclude:[m(s,e,"node_modules"),m(s,e,"dist"),m(s,e,".testkit")]};return d.writeFileSync(s,`${JSON.stringify(r,null,2)}
|
|
2
|
+
`),s}function Y({productDir:e,cwd:n,engineId:t,extendsPath:s,files:o,outputDir:r,scopeName:a}){const c=i.join(r,`${t}.${te(a)}.tsconfig.json`),u=t==="playwright",f=u&&M(n),g={...s?{extends:l(c,s)}:{},compilerOptions:{...buildTypecheckCompilerOptions({engineId:t,tsconfigPath:c,extendsPath:s,libs:Z(t),...f?{extraPaths:nextRuntimeTypePaths({tsconfigPath:c,serviceDir:n})}:{},types:Q(t)}),...u?{allowJs:!0,checkJs:!1}:{}},files:[...ae(t,c),...o.map(p=>l(c,p))],include:f?[...d.existsSync(i.join(n,"next-env.d.ts"))?[l(c,i.join(n,"next-env.d.ts"))]:[],m(c,n,"tests/playwright-fixtures/**/*.ts"),m(c,n,"tests/playwright-fixtures/**/*.tsx"),m(c,n,"tests/playwright-fixtures/**/*.mts"),m(c,n,".next/types/**/*.ts"),m(c,n,".next/dev/types/**/*.ts"),m(c,e,".next-testkit/**/dist/types/**/*.ts"),m(c,e,".next-testkit/**/dist/dev/types/**/*.ts")]:[],exclude:["node_modules",".next/cache",".next/dev"]};return d.writeFileSync(c,`${JSON.stringify(g,null,2)}
|
|
3
|
+
`),c}function W(e,n){const t=new Map,s=new Map;for(const o of n){const r=i.resolve(e,o.path),a=[o.engineId,r].join("\0");s.set(a,{...o,path:r})}for(const o of s.values()){const r=o.path,a=o.engineId==="playwright"?w(i.dirname(r),e,["tsconfig.playwright.json"]):null,c=a??w(i.dirname(r),e),u=c?i.dirname(c):e,f=ee(e,u),g=`${o.engineId}\0${c??""}\0${f}`,p=t.get(g);if(p){p.files.push(r);continue}t.set(g,{cwd:u,engineId:o.engineId,extendsPath:c,files:[r,...a?V(a):[]],scopeName:f})}return[...t.values()].map(o=>({...o,files:[...new Set(o.files)].sort((r,a)=>r.localeCompare(a))})).sort((o,r)=>v(o.engineId)-v(r.engineId)||o.scopeName.localeCompare(r.scopeName))}function V(e){const n=E(),t=o=>{throw new Error(`${e}: ${n.flattenDiagnosticMessageText(o.messageText,`
|
|
4
|
+
`)}`)},s=n.getParsedCommandLineOfConfigFile(e,{},{...n.sys,onUnRecoverableConfigFileDiagnostic:t});for(const o of s?.errors??[])o.code!==18002&&o.code!==18003&&t(o);return s?.fileNames??[]}function Q(e){switch(e){case"k6":return["k6","xk6-sql"];case"node-vitest":case"playwright":return["node"]}}function Z(e){return e==="playwright"?["ES2022","DOM","DOM.Iterable"]:["ES2022"]}function v(e){return["k6","node-vitest","playwright"].indexOf(e)}function X(e,n){return n==="repo"?e:`${e}:${n}`}function ee(e,n){const t=i.relative(e,n);return t===""?"repo":t.split(i.sep).join("/")}function te(e){const n=e.replace(/[^a-zA-Z0-9_-]+/g,"-");if(n===e)return e;const t=j("sha256").update(e).digest("hex").slice(0,12);return`${n}-${t}`}function ne({label:e,cwd:n,tsconfigPath:t},s,o){const r=se(n);return U({args:[...r.args,"--noEmit","-p",t],cwd:n,displayLabel:`Testkit typecheck ${e}`,env:process.env,executable:r.command,signal:o,timeoutMs:s}).then(a=>{const c=G(`Testkit typecheck ${e}`,a);if(c)throw c;return{label:e,tsconfigPath:t,ok:a.exitCode===0,exitCode:a.exitCode,stdout:a.stdout,stderr:a.stderr}})}function b(e){const n=e??J;if(!Number.isSafeInteger(n)||n<=0)throw new Error("Testkit typecheck timeout must be a positive safe integer in milliseconds");return n}function se(e){let n=e;for(;;){const s=i.join(n,"node_modules","typescript","bin","tsc");if(d.existsSync(s))return{command:process.execPath,args:[s]};const o=i.dirname(n);if(o===n)break;n=o}const t=i.join(h,"node_modules","typescript","bin","tsc");if(d.existsSync(t))return{command:process.execPath,args:[t]};throw new Error(`TypeScript compiler not found from ${e} upward`)}function P(e,n=["tsconfig.json","tsconfig.build.json"]){for(const t of n){const s=i.join(e,t);if(d.existsSync(s))return s}return null}function w(e,n,t){const s=i.resolve(n);let o=i.resolve(e);for(;;){if(!T(s,o))return null;const r=P(o,t);if(r)return r;if(o===s)return null;const a=i.dirname(o);if(a===o)return null;o=a}}function l(e,n){const t=i.relative(i.dirname(e),n).split(i.sep).join("/");return t.startsWith("../")||i.isAbsolute(t)?t:`./${t}`}function m(e,n,t){return i.join(l(e,n),t).split(i.sep).join("/")}function oe(e){const n={};for(const[t,s]of Object.entries(q))n[t]=[l(e,i.join(h,"dist",...s))];return n}const re=Object.freeze({packages:Object.freeze(O.filter(({authoringResolution:e})=>e==="package-exports").map(({packageName:e})=>Object.freeze({name:e}))),supportedExports:"exact-manifest-exports"}),ie=Object.freeze({packages:Object.freeze($.map(({packageName:e})=>Object.freeze({name:e}))),supportedExports:"exact-manifest-exports"});function ce(e,n){const t=ue(e);if(t===null)return{};const s={},o=l(n,i.join(h,"dist","app","typecheck","unavailable.d.ts"));for(const r of t.packages){const a=le(r.name),c=fe(r.name,a);s[`${r.name}/*`]=[o];for(const u of Object.keys(c).sort()){if(u!=="."&&!u.startsWith("./")||u.includes("*"))continue;const f=u==="."?r.name:`${r.name}${u.slice(1)}`,g=ge(f,r.name,a);s[f]=[g===null?o:l(n,g)]}}return s}function ae(e,n){return e==="k6"?[l(n,i.join(h,"dist",...L))]:[]}function pe(e,n){return e==="k6"?{[N]:[l(n,i.join(h,"dist",...D))]}:{}}function ue(e){switch(e){case"node-vitest":return re;case"playwright":return ie;case"k6":case void 0:return null}}function le(e){const n=de(e);let t;try{t=x.resolve(`${e}/package.json`)}catch(r){throw new Error(`Testkit typecheck requires managed public package ${e}`,{cause:r})}const s=d.realpathSync.native(i.dirname(t)),o=JSON.parse(d.readFileSync(i.join(s,"package.json"),"utf8"));return requireManagedPublicPackageIdentity(o,e,n),s}export function requireManagedPublicPackageIdentity(e,n,t){if(!k(e)||e.name!==n||e.version!==t)throw new Error(`Testkit managed public package has invalid identity: expected ${n}@${t}`)}function de(e){const n=JSON.parse(d.readFileSync(i.join(h,"package.json"),"utf8"));if(!k(n)||!k(n.dependencies))throw new Error("Testkit package has invalid dependency metadata");const t=n.dependencies[e];if(typeof t!="string"||!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u.test(t))throw new Error(`Testkit managed public package ${e} must be an exact root dependency`);return t}function fe(e,n){const t=JSON.parse(d.readFileSync(i.join(n,"package.json"),"utf8"));if(typeof t!="object"||t===null||Array.isArray(t)||!("name"in t)||t.name!==e||!("exports"in t)||typeof t.exports!="object"||t.exports===null||Array.isArray(t.exports))throw new Error(`Testkit managed public package has invalid exports: ${e}`);return t.exports}function ge(e,n,t){const s=E(),o=s.resolveModuleName(e,i.join(h,"__testkit_managed_public_types__.ts"),{module:s.ModuleKind.ESNext,moduleResolution:s.ModuleResolutionKind.Bundler,resolveJsonModule:!0,target:s.ScriptTarget.ES2022},s.sys).resolvedModule;if(o===void 0)return null;const r=d.realpathSync.native(o.resolvedFileName);if(!T(t,r))throw new Error(`Managed public package type ${e} resolved outside ${n}`);return r}export function buildTypecheckCompilerOptions({engineId:e,tsconfigPath:n,extendsPath:t,rootDir:s,extraPaths:o={},libs:r,types:a}){const u={...t?ye({tsconfigPath:n,extendsPath:t}):{},...o,...oe(n),...ce(e,n),...pe(e,n)};return{baseUrl:null,lib:[...r],module:"ESNext",moduleResolution:"Bundler",noEmit:!0,...s?{rootDir:s}:{},target:"ES2022",typeRoots:me(a,n),types:[...a],paths:u}}function me(e,n){const t=new Set;for(const s of e){if(s==="k6"||s==="xk6-sql"){const c=i.join(h,"dist","engines","k6","runtime","ambient-types");if(!d.existsSync(i.join(c,s,"index.d.ts")))throw new Error(`Testkit typecheck requires bundled ${s} authoring types; rebuild the installed Testkit package`);t.add(c);continue}const o=he(s);let r;try{r=x.resolve(`${o}/package.json`)}catch(c){throw new Error(`Testkit typecheck requires installed type package ${o}`,{cause:c})}const a=i.dirname(r);t.add(i.dirname(a))}return[...t].map(s=>l(n,s))}function he(e){return e==="node"||e==="k6"?`@types/${e}`:e}function ye({tsconfigPath:e,extendsPath:n}){const t=E(),s=t.getParsedCommandLineOfConfigFile(n,{},{...t.sys,onUnRecoverableConfigFileDiagnostic:()=>{}}),o=s?.options.paths;if(!o||Object.keys(o).length===0)return{};const r=s.options.baseUrl||(typeof s.options.pathsBasePath=="string"?s.options.pathsBasePath:i.dirname(n)),a={};for(const[c,u]of Object.entries(o))a[c]=u.map(f=>l(e,i.resolve(r,f)));return a}export function nextRuntimeTypePaths({tsconfigPath:e,serviceDir:n}){const t=ke(n,[i.join("next","types.d.ts"),i.join("next","types.js")]);if(!t)return{};const s=l(e,t);return{"next/types":[s],"next/types.js":[s]}}function ke(e,n){let t=i.resolve(e);for(;;){for(const o of n){const r=i.join(t,"node_modules",o);if(d.existsSync(r))return r}const s=i.dirname(t);if(s===t)return null;t=s}}
|
|
@@ -79,6 +79,25 @@ export declare const commandCatalog: Readonly<{
|
|
|
79
79
|
lint: import("./framework/command.js").CommandDefinition<{
|
|
80
80
|
readonly dir: import("./framework/definitions.js").FlagDefinition<string | undefined>;
|
|
81
81
|
}, undefined>;
|
|
82
|
+
"report prepare": import("./framework/command.js").CommandDefinition<{
|
|
83
|
+
readonly dir: import("./framework/definitions.js").FlagDefinition<string | undefined>;
|
|
84
|
+
readonly template: import("./framework/definitions.js").FlagDefinition<string | undefined>;
|
|
85
|
+
readonly instructions: import("./framework/definitions.js").FlagDefinition<string | undefined>;
|
|
86
|
+
readonly bug: import("./framework/definitions.js").FlagDefinition<string[] | undefined>;
|
|
87
|
+
readonly run: import("./framework/definitions.js").FlagDefinition<string | undefined>;
|
|
88
|
+
readonly artifact: import("./framework/definitions.js").FlagDefinition<string[] | undefined>;
|
|
89
|
+
}, {
|
|
90
|
+
readonly directory: import("./framework/definitions.js").ArgumentDefinition<string>;
|
|
91
|
+
}>;
|
|
92
|
+
"report publish": import("./framework/command.js").CommandDefinition<{
|
|
93
|
+
readonly dir: import("./framework/definitions.js").FlagDefinition<string | undefined>;
|
|
94
|
+
readonly repository: import("./framework/definitions.js").FlagDefinition<string>;
|
|
95
|
+
readonly branch: import("./framework/definitions.js").FlagDefinition<string>;
|
|
96
|
+
readonly path: import("./framework/definitions.js").FlagDefinition<string>;
|
|
97
|
+
readonly message: import("./framework/definitions.js").FlagDefinition<string | undefined>;
|
|
98
|
+
}, {
|
|
99
|
+
readonly directory: import("./framework/definitions.js").ArgumentDefinition<string>;
|
|
100
|
+
}>;
|
|
82
101
|
"run status": import("./framework/command.js").CommandDefinition<{
|
|
83
102
|
readonly dir: import("./framework/definitions.js").FlagDefinition<string | undefined>;
|
|
84
103
|
readonly service: import("./framework/definitions.js").FlagDefinition<string | undefined>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{Args as
|
|
1
|
+
import{Args as i,Flags as e}from"./framework/definitions.js";import{defineCommand as t}from"./framework/command.js";import{sharedFlags as r,runFlags as a}from"./command-flags.js";import{publicTestTypeList as n,publicTestTypeListText as s}from"../domain/test-types.js";export const commandCatalog=Object.freeze({artifacts:t({summary:"List, verify, or inspect artifacts from immutable Testkit runs",enableJsonFlag:!0,args:{file:i.string({description:"Optional test file selector",required:!1})},flags:{...r,kind:e.string({description:"Limit artifacts to one kind"}),format:e.string({description:"Output format",options:["list","text"],default:"list"}),run:e.string({description:"Read one immutable run ID instead of latest"}),list:e.boolean({description:"List retained immutable runs",default:!1}),verify:e.boolean({description:"Verify the selected run manifest and checksums",default:!1})}}),"bug file":t({summary:"Run a recorded bug's test and file the bug as an issue",enableJsonFlag:!0,args:{id:i.string({description:"Draft id that `testkit bug record` printed",required:!0})},flags:{dir:e.string({description:"Product directory"})}}),"bug guide":t({summary:"Print the bug record format, the severities, and the category lists",enableJsonFlag:!0}),"bug record":t({summary:"Record a confirmed bug as a local draft",enableJsonFlag:!0,args:{file:i.string({description:"Bug record JSON file, or - to read standard input",required:!0})},flags:{dir:e.string({description:"Product directory"})}}),"campaign finish":t({summary:"Finish the active campaign and write its pull request summary",enableJsonFlag:!0,flags:{dir:e.string({description:"Product directory"})}}),"campaign start":t({summary:"Start a bug-hunting campaign and print the agent's brief",enableJsonFlag:!0,flags:{dir:e.string({description:"Product directory"}),id:e.string({description:"Campaign id, instead of one made from the target and the date"}),target:e.string({description:"What the agent should hunt, in one line of at most 200 characters",required:!0})}}),"campaign status":t({summary:"Show the active campaign, its recorded bugs, and the area map coverage",enableJsonFlag:!0,flags:{dir:e.string({description:"Product directory"})}}),"ci report":t({summary:"Post the last run as a commit status, a step summary, and one pull request comment",enableJsonFlag:!0,flags:{context:e.string({description:"Commit status context, instead of testkit"}),dir:e.string({description:"Product directory"})}}),cleanup:t({summary:"Clean stale local testkit state",enableJsonFlag:!0,flags:{...r,"dry-run":e.boolean({description:"Show cleanup actions without deleting state",default:!1}),cache:e.string({description:"Clean cache state: runtime, bundles, or all",multiple:!0,options:["runtime","bundles","all"]}),resources:e.boolean({description:"Clean Testkit-owned Docker resources that are stale or orphaned",default:!1})}}),"db schema refresh":t({summary:"Refresh a source database schema cache",enableJsonFlag:!0,flags:r}),"db schema verify":t({summary:"Verify local schema replay against the source database schema",enableJsonFlag:!0,flags:{...r,"skip-schema-source-verify":e.boolean({description:"Skip the check that local schema replay matches the source database schema",default:!1})}}),destroy:t({summary:"Destroy local testkit state",enableJsonFlag:!0,flags:r}),discover:t({summary:"Discover managed tests and report their metadata",enableJsonFlag:!0,flags:{...r,type:e.string({char:"t",multiple:!0,description:`Filter by suite type(s): ${s({includeAll:!0})}`}),suite:e.string({char:"s",multiple:!0,description:"Filter by suite selector(s)"}),file:e.string({char:"f",multiple:!0,description:"Filter by exact file path(s)"}),"runnable-only":e.boolean({description:"Show only files not skipped by repo rules",default:!1}),strict:e.boolean({description:"Fail instead of reporting discovery/config diagnostics",default:!1}),output:e.string({description:"Write machine-readable discovery JSON to a file"}),"output-mode":e.string({description:"Human-readable output mode",options:["compact","verbose"],default:"compact"})}}),doctor:t({summary:"Run built-in config, discovery, and hygiene checks",enableJsonFlag:!0,flags:{dir:e.string({description:"Product directory"}),typecheck:e.boolean({description:"Include generated TypeScript checks",default:!0,allowNo:!0})}}),"guard unsafe-entrypoint":t({summary:"Fail a local script that must be run through Testkit",flags:{command:e.string({char:"c",description:"Disabled command name shown in the error message",required:!0}),use:e.string({char:"u",description:"Replacement command shown in the error message",required:!0})}}),lint:t({summary:"Run built-in Testkit repository hygiene checks",enableJsonFlag:!0,flags:{dir:e.string({description:"Product directory"})}}),"report prepare":t({summary:"Prepare a local report with optional template, bug records, and selected run evidence",enableJsonFlag:!0,args:{directory:i.string({description:"New bundle directory",required:!0})},flags:{dir:e.string({description:"Product directory containing bug records and runs"}),template:e.string({description:"Copy this file unchanged to report.md"}),instructions:e.string({description:"Print this repository-owned drafting guide for the author"}),bug:e.string({description:"Include this recorded bug ID as context",multiple:!0}),run:e.string({description:"Immutable run ID containing the selected artifacts"}),artifact:e.string({description:"Exact artifact ref.path from testkit artifacts --json",multiple:!0})}}),"report publish":t({summary:"Publish a prepared directory at one Git revision without changing your checkout",enableJsonFlag:!0,args:{directory:i.string({description:"Directory containing only the files to publish",required:!0})},flags:{dir:e.string({description:"Product directory used to resolve provider credentials"}),repository:e.string({description:"Destination GitHub or Gitea repository URL, or owner/name",required:!0}),branch:e.string({description:"Destination branch",required:!0}),path:e.string({description:"Bundle directory within the destination repository",required:!0}),message:e.string({description:"Publication commit message"})}}),"run status":t({summary:"Show active Testkit run progress",enableJsonFlag:!0,flags:{...r,run:e.string({description:"Specific run id to show"}),full:e.boolean({description:"Include the full live status payload in JSON output"})}}),"run stop":t({summary:"Stop active Testkit run work",enableJsonFlag:!0,flags:{...r,run:e.string({description:"Specific run id to stop; defaults to the newest active run"}),all:e.boolean({description:"Stop all active runs for this product",default:!1}),reason:e.string({description:"Operator-visible reason recorded with cancelled runs and dirty slots"})}}),run:t({summary:"Run test suites",enableJsonFlag:!0,args:{type:i.string({description:`Optional suite type shortcut: ${s({includeAll:!0})}`,required:!1,options:n({includeAll:!0})})},flags:a}),"setup playwright":t({summary:"Install the managed Playwright Chromium runtime",enableJsonFlag:!0,flags:{dir:e.string({description:"Product directory"}),force:e.boolean({default:!1,description:"Force Playwright to repair the browser installation"}),"with-deps":e.boolean({default:!1,description:"Install required host dependencies with Chromium"})}}),status:t({summary:"Show local testkit state",enableJsonFlag:!0,flags:r}),stop:t({summary:"Stop all Testkit-owned runs",enableJsonFlag:!0,flags:{...r,reason:e.string({description:"Operator-visible reason recorded with cancelled runs"})}}),typecheck:t({summary:"Typecheck testkit config, helpers, and suites",enableJsonFlag:!0,flags:{dir:e.string({description:"Product directory"})}}),workspace:t({summary:"Inspect Testkit workstation data for a product",enableJsonFlag:!0,flags:{...r,"target-limit":e.integer({description:"Maximum number of quick-run target previews",default:24}),"no-status":e.boolean({description:"Skip runtime status collection",default:!1})}})});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{commandCatalog as i}from"./command-catalog.js";export const commandRegistry=Object.freeze([t("artifacts",()=>import("./commands/artifacts.js")),t("bug file",()=>import("./commands/bug/file.js")),t("bug guide",()=>import("./commands/bug/guide.js")),t("bug record",()=>import("./commands/bug/record.js")),t("campaign finish",()=>import("./commands/campaign/finish.js")),t("campaign start",()=>import("./commands/campaign/start.js")),t("campaign status",()=>import("./commands/campaign/status.js")),t("ci report",()=>import("./commands/ci/report.js")),t("cleanup",()=>import("./commands/cleanup.js")),t("db schema refresh",()=>import("./commands/db/schema/refresh.js")),t("db schema verify",()=>import("./commands/db/schema/verify.js")),t("destroy",()=>import("./commands/destroy.js")),t("discover",()=>import("./commands/discover.js")),t("doctor",()=>import("./commands/doctor.js")),t("guard unsafe-entrypoint",()=>import("./commands/guard/unsafe-entrypoint.js")),t("lint",()=>import("./commands/lint.js")),t("run status",()=>import("./commands/run/status.js")),t("run stop",()=>import("./commands/run/stop.js")),t("run",()=>import("./commands/run.js")),t("setup playwright",()=>import("./commands/setup/playwright.js")),t("status",()=>import("./commands/status.js")),t("stop",()=>import("./commands/stop.js")),t("typecheck",()=>import("./commands/typecheck.js")),t("workspace",()=>import("./commands/workspace.js"))]),removedCommands=new Map([["cloud","The testkit cloud commands were removed in Testkit 0.4.0. Testkit no longer publishes runs to Testkit Cloud."]]);function t(r,o){return Object.freeze({...i[r],id:r.split(" "),load:o})}
|
|
1
|
+
import{commandCatalog as i}from"./command-catalog.js";export const commandRegistry=Object.freeze([t("artifacts",()=>import("./commands/artifacts.js")),t("bug file",()=>import("./commands/bug/file.js")),t("bug guide",()=>import("./commands/bug/guide.js")),t("bug record",()=>import("./commands/bug/record.js")),t("campaign finish",()=>import("./commands/campaign/finish.js")),t("campaign start",()=>import("./commands/campaign/start.js")),t("campaign status",()=>import("./commands/campaign/status.js")),t("ci report",()=>import("./commands/ci/report.js")),t("cleanup",()=>import("./commands/cleanup.js")),t("db schema refresh",()=>import("./commands/db/schema/refresh.js")),t("db schema verify",()=>import("./commands/db/schema/verify.js")),t("destroy",()=>import("./commands/destroy.js")),t("discover",()=>import("./commands/discover.js")),t("doctor",()=>import("./commands/doctor.js")),t("guard unsafe-entrypoint",()=>import("./commands/guard/unsafe-entrypoint.js")),t("lint",()=>import("./commands/lint.js")),t("report prepare",()=>import("./commands/report/prepare.js")),t("report publish",()=>import("./commands/report/publish.js")),t("run status",()=>import("./commands/run/status.js")),t("run stop",()=>import("./commands/run/stop.js")),t("run",()=>import("./commands/run.js")),t("setup playwright",()=>import("./commands/setup/playwright.js")),t("status",()=>import("./commands/status.js")),t("stop",()=>import("./commands/stop.js")),t("typecheck",()=>import("./commands/typecheck.js")),t("workspace",()=>import("./commands/workspace.js"))]),removedCommands=new Map([["cloud","The testkit cloud commands were removed in Testkit 0.4.0. Testkit no longer publishes runs to Testkit Cloud."]]);function t(r,o){return Object.freeze({...i[r],id:r.split(" "),load:o})}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { CommandContext } from "../../command-catalog.js";
|
|
2
|
+
export default function run({ args, flags, json, log }: CommandContext<"report prepare">): {
|
|
3
|
+
directory: string;
|
|
4
|
+
files: string[];
|
|
5
|
+
bugs: string[];
|
|
6
|
+
run: string | null;
|
|
7
|
+
instructions: {
|
|
8
|
+
path: string;
|
|
9
|
+
text: string;
|
|
10
|
+
} | null;
|
|
11
|
+
ok: boolean;
|
|
12
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import{prepareReport as o}from"../../../app/reports/prepare.js";export default function s({args:i,flags:e,json:n,log:t}){const r=o({directory:i.directory,...e});return n||(t(`Prepared ${r.directory}
|
|
2
|
+
${r.files.join(`
|
|
3
|
+
`)}
|
|
4
|
+
Write and review the narrative before publication.`),r.instructions&&t(`Drafting instructions from ${r.instructions.path}:
|
|
5
|
+
${r.instructions.text}`)),{ok:!0,...r}}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { CommandContext } from "../../command-catalog.js";
|
|
2
|
+
export default function run({ args, flags, json, log }: CommandContext<"report publish">): Promise<{
|
|
3
|
+
revision: string;
|
|
4
|
+
previous: string | null;
|
|
5
|
+
changed: boolean;
|
|
6
|
+
repository: string;
|
|
7
|
+
branch: string;
|
|
8
|
+
path: string;
|
|
9
|
+
url: string;
|
|
10
|
+
files: number;
|
|
11
|
+
bytes: number;
|
|
12
|
+
ok: boolean;
|
|
13
|
+
}>;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{publishReport as n}from"../../../app/reports/publish.js";export default async function s({args:i,flags:r,json:t,log:o}){const e=await n({directory:i.directory,repository:r.repository,branch:r.branch,path:r.path,dir:r.dir,message:r.message});return t||o(`${e.changed?"Published":"Already published"} ${String(e.files)} files at ${e.revision}
|
|
2
|
+
${e.url}`),{ok:!0,...e}}
|
package/dist/cli/json-errors.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{serializeError as
|
|
1
|
+
import{hasErrorCode as i,serializeError as n}from"../kernel/error-serialization.js";export function serializeCliError(r){return n(r)}export function writeStandardOutput(r){return new Promise((o,s)=>{const e=t=>{t?i(t,"EPIPE")?o():s(t):(process.stdout.off("error",e),o())};process.stdout.once("error",e),process.stdout.write(r,e)})}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type RunEnvironmentInput } from "../../../app/run/orchestrator.js";
|
|
1
2
|
import type { RunRequest } from "../../../app/run/request.js";
|
|
2
3
|
export interface RunTerminalStream {
|
|
3
4
|
columns?: number;
|
|
@@ -5,6 +6,7 @@ export interface RunTerminalStream {
|
|
|
5
6
|
}
|
|
6
7
|
export interface ExecuteRunRequestOptions {
|
|
7
8
|
debug?: boolean;
|
|
9
|
+
environments?: RunEnvironmentInput;
|
|
8
10
|
json?: boolean;
|
|
9
11
|
outputMode?: string;
|
|
10
12
|
terminal?: {
|
|
@@ -13,7 +15,7 @@ export interface ExecuteRunRequestOptions {
|
|
|
13
15
|
};
|
|
14
16
|
}
|
|
15
17
|
export type RunOutputMode = "compact" | "debug" | "events" | "json";
|
|
16
|
-
export declare function executeRunRequest(request: Readonly<RunRequest>, { outputMode: requestedMode, json, debug, terminal, }?: Readonly<ExecuteRunRequestOptions>): Promise<{
|
|
18
|
+
export declare function executeRunRequest(request: Readonly<RunRequest>, { outputMode: requestedMode, json, debug, environments, terminal, }?: Readonly<ExecuteRunRequestOptions>): Promise<{
|
|
17
19
|
exitCode: number;
|
|
18
20
|
failure?: import("../../../kernel/error-serialization.js").SerializedError;
|
|
19
21
|
results: import("../../../app/run/results.js").FinalizedServiceResult[];
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{runAll as p}from"../../../app/run/orchestrator.js";export async function executeRunRequest(t,{outputMode:
|
|
2
|
-
`),{outputMode:n,runId:
|
|
1
|
+
import{runAll as p}from"../../../app/run/orchestrator.js";export async function executeRunRequest(t,{outputMode:u="compact",json:e=!1,debug:s=!1,environments:r,terminal:a={}}={}){const o=a.stdout||process.stdout,n=resolveRunOutputMode(u,e,s),f=n==="json"?()=>{}:n==="events"?(await import("../../renderers/run/events.js")).createRunEventsListener({stdout:o}):(await import("../../renderers/run/text-reporter.js")).createRunTextListener({outputMode:n,stdout:o}),c=await p(t.configs,t.typeValues,t.suiteSelectors,{debug:n==="debug",onEvent:f,...r===void 0?{}:{environments:r},...t.runOptions},t.allConfigs),i=c.runArtifact.archive.runId;return(n==="compact"||n==="debug")&&o.write(`Run ID: ${i}
|
|
2
|
+
`),{outputMode:n,runId:i,...c}}export function resolveRunOutputMode(t,u,e){return u?"json":e?"debug":t==="debug"||t==="events"||t==="json"?t:"compact"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export function renderDoctorResult(e){const n=[`testkit doctor ${e.ok?"passed":"failed"} for ${e.productDir}`];for(const
|
|
1
|
+
import{isUnknownRecord as s}from"../../../kernel/unknown-values.js";export function renderDoctorResult(e){const n=[`testkit doctor ${e.ok?"passed":"failed"} for ${e.productDir}`];for(const o of e.checks){n.push(`${o.level.toUpperCase()} ${o.code} ${o.message}`);for(const t of o.details?.slice(0,5)??[]){const r=i(t);r&&n.push(r)}}return n}function i(e){if(!s(e)||typeof e.ruleId!="string")return null;const n=typeof e.file=="string"?e.file:"unknown",o=typeof e.line=="number"?String(e.line):"?",t=typeof e.message=="string"?e.message:"";return` ${e.ruleId} ${n}:${o} ${t}`}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{sanitizeErrorMessage as p}from"../../../kernel/error-serialization.js";export function formatDuration(e){const t=Math.max(0,Math.round(e/1e3)),i=Math.floor(t/60),n=t%60;return i===0?`${String(n)}s`:`${String(i)}m ${String(n).padStart(2,"0")}s`}function
|
|
1
|
+
import{sanitizeErrorMessage as p}from"../../../kernel/error-serialization.js";import{isUnknownRecord as q}from"../../../kernel/unknown-values.js";export function formatDuration(e){const t=Math.max(0,Math.round(e/1e3)),i=Math.floor(t/60),n=t%60;return i===0?`${String(n)}s`:`${String(i)}m ${String(n).padStart(2,"0")}s`}function I(e){const t=e.skippedSuiteCount||0,i=e.completedSuiteCount-e.failedSuiteCount-t,n=e.suiteCount-e.completedSuiteCount;let s=`${String(i)}/${String(e.suiteCount)} suites passed`;return(e.totalFileCount||0)>0&&(s+=`, ${String(e.passedFileCount)}/${String(e.totalFileCount)} files passed`),t>0?s+=`, ${String(t)} ${S(t,"suite","suites")} skipped`:(e.skippedFileCount||0)>0&&(s+=`, ${String(e.skippedFileCount)} ${S(e.skippedFileCount,"file","files")} skipped`),n>0?s+=`, ${String(n)} ${S(n,"suite","suites")} not run`:(e.notRunFileCount||0)>0&&(s+=`, ${String(e.notRunFileCount)} ${S(e.notRunFileCount,"file","files")} not run`),s}function P(e){return e.reduce((t,i)=>Math.max(t,i.name.length),4)}export function buildRunSummaryData(e,t,i=null){const n=M(e),s=j(e);return{result:i==="failed"||i==="interrupted"||n.failedServices>0||n.infraFailedServices>0?"FAILED":"PASSED",totalServices:n.totalServices,failedServices:n.failedServices,infraFailedServices:n.infraFailedServices,blockedByRuntimeServices:n.blockedByRuntimeServices,passed:n.passedFiles,failed:n.failedFiles,skipped:n.skippedFiles,notRun:n.notRunFiles,files:n.totalFiles,duration:formatDuration(t),serviceErrors:s}}export function buildDebugRunSummaryLines(e,t,i=null){const n=e.length,s=e.filter(r=>!r.skipped),d=e.filter(r=>r.skipped),c=s.filter(r=>r.failed),u=s.filter(r=>r.infraFailed),f=s.filter(r=>!r.failed&&!r.infraFailed),C=s.reduce((r,o)=>r+o.suiteCount,0),R=s.reduce((r,o)=>r+o.completedSuiteCount,0),F=s.reduce((r,o)=>r+(o.skippedSuiteCount||0),0),E=s.reduce((r,o)=>r+o.failedSuiteCount,0),y=R-E-F,k=s.reduce((r,o)=>r+(o.totalFileCount||0),0),D=s.reduce((r,o)=>r+(o.passedFileCount||0),0),m=s.reduce((r,o)=>r+(o.skippedFileCount||0),0),a=["","\u2550\u2550 Summary \u2550\u2550",[`services ${String(f.length)}/${String(s.length)} passed`,`suites ${String(y)}/${String(C)} passed`,k>0?`files ${String(D)}/${String(k)} passed`:null,F>0?`suites ${String(F)} skipped`:null,m>0?`files ${String(m)} skipped`:null,d.length>0?`${String(d.length)} skipped`:null,`duration ${formatDuration(t)}`].filter(Boolean).join(" \xB7 ")];for(const r of e){const o=N(r)?"SKIP":r.failed?"FAIL":r.infraFailed?"INFRA":"PASS",b=r.skipped?"no matching suites":I(r);if(a.push(`${o.padEnd(4)} ${r.name.padEnd(P(e))} ${b} \xB7 ${formatDuration(r.durationMs)}`),!(!r.failed&&!r.infraFailed)){for(const l of r.suites.filter($=>$.failed)){const $=l.failedFiles.length>0?` (${l.failedFiles.join(", ")})`:"";a.push(` - ${l.testType}:${l.name}${$} \xB7 ${formatDuration(l.durationMs)}`),l.error&&a.push(` ${l.error}`)}for(const l of r.errors)a.push(` - worker error: ${l}`)}}return i==="failed"||i==="interrupted"||c.length>0||u.length>0?(a.push("",c.length===0&&u.length===0?"Result: FAILED (run gate)":`Result: FAILED (${x({failedServices:c.length,infraFailedServices:u.length,totalServices:n})})`),a):(a.push("","Result: PASSED"),a)}export function buildFailurePresentation(e){const t=L(e.failureDetails||[]),i=t[0]||null,n=t.map(u=>p((u.message||u.title).trim())),s=[],d=w(i);d&&s.push(d);const c=B(i);c&&s.push(c);for(const u of t.slice(1)){const f=h(u.message||u.title,220);f&&!s.includes(f)&&s.push(f)}return{primary:h(A(e,{error:e.suiteError||null},i,n),240),details:s}}function M(e){const t=e.filter(i=>!i.skipped);return{totalServices:e.length,failedServices:t.filter(i=>i.failed).length,infraFailedServices:t.filter(i=>i.infraFailed).length,blockedByRuntimeServices:t.filter(i=>i.blockedByRuntime).length,totalFiles:t.reduce((i,n)=>i+(n.totalFileCount||0),0),passedFiles:t.reduce((i,n)=>i+(n.passedFileCount||0),0),failedFiles:t.reduce((i,n)=>i+(n.failedFileCount||0),0),skippedFiles:t.reduce((i,n)=>i+(n.skippedFileCount||0),0),notRunFiles:t.reduce((i,n)=>i+(n.notRunFileCount||0),0)}}function x(e){const t=[`${String(e.failedServices)}/${String(e.totalServices)} services failed`];return(e.infraFailedServices||0)>0&&t.push(`${String(e.infraFailedServices)} infra-blocked`),t.join(", ")}function A(e,t,i,n){if(i?.message)return p(i.message.trim());const s=p(e.error||"");return s&&!z(s)?s:n.length>0?n[0]:s||p(t.error||"Failed")}function L(e){return[...e].sort((t,i)=>v(t)-v(i)||(t.key||"").localeCompare(i.key||""))}function v(e){return e.kind==="http-assertion"?1:e.request&&e.response?2:e.location||e.stack?3:e.message?4:5}function w(e){const t=g(e?.response,"bodyPreview");return t?`response: ${h(t,220)}`:null}function B(e){const t=e?.request,i=g(t,"method"),n=g(t,"path");if(!i||!n)return null;const s=g(t,"requestId");return s?`logs: requestId=${s}`:`request: ${i} ${n}`}function z(e){return/k6 thresholds failed:/.test(e)}function h(e,t=180){return e.replace(/\s+/g," ").trim().slice(0,t)}function j(e){return e.reduce((t,i)=>t+i.errors.length,0)}function S(e,t,i){return e===1?t:i}function N(e){return e.skipped?!0:!e.failed&&(e.skippedSuiteCount||0)>0&&(e.skippedSuiteCount||0)===e.suiteCount&&(e.notRunFileCount||0)===0}function g(e,t){if(!q(e))return null;const i=e[t];return typeof i=="string"&&i.length>0?i:null}
|