Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 41 additions & 5 deletions scripts/test/snapshot-dependencies.mts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export interface SnapshotDependency {
readonly source: string
readonly versionPattern: RegExp
readonly snapshots: readonly string[]
readonly versionSnapshots?: readonly string[]
readonly testNamePattern: string
}

Expand All @@ -23,9 +24,29 @@ export const SNAPSHOT_DEPENDENCIES = {
],
testNamePattern: 'captures native help for (fix|scan create|scan reach)',
},
patch: {
packageName: '@socketsecurity/socket-patch',
source: 'package.json',
versionPattern: /"@socketsecurity\/socket-patch"\s*:\s*"([^"]+)"/,
snapshots: [
'test/native-snapshots/socket-patch-apply-help.help.svg',
'test/native-snapshots/socket-patch-rollback-help.help.svg',
'test/native-snapshots/socket-patch-get-help.help.svg',
'test/native-snapshots/socket-patch-scan-help.help.svg',
'test/native-snapshots/socket-patch-list-help.help.svg',
'test/native-snapshots/socket-patch-remove-help.help.svg',
'test/native-snapshots/socket-patch-setup-help.help.svg',
'test/native-snapshots/socket-patch-repair-help.help.svg',
'test/native-snapshots/socket-patch-version.version.svg',
],
versionSnapshots: ['test/native-snapshots/socket-patch-version.version.svg'],
testNamePattern:
'captures bundled patch help for (apply|rollback|get|scan|list|remove|setup|repair)|captures bundled socket-patch version',
},
} as const satisfies Record<string, SnapshotDependency>

export type SnapshotDependencyName = keyof typeof SNAPSHOT_DEPENDENCIES
export type SnapshotDependencyName = 'coana' | 'patch' | 'socket-patch'
type SnapshotDependencyMapName = keyof typeof SNAPSHOT_DEPENDENCIES

export interface StaleSnapshotDependency {
readonly dependencyName: SnapshotDependencyName
Expand All @@ -38,7 +59,10 @@ export async function findStaleSnapshotDependencies(): Promise<
StaleSnapshotDependency[]
> {
const findings = await Promise.all(
Object.entries(SNAPSHOT_DEPENDENCIES).map(async ([name, dependency]) => {
(Object.entries(SNAPSHOT_DEPENDENCIES) as Array<[
string,
SnapshotDependency,
]>).map(async ([name, dependency]) => {
const source = await fs.readFile(
path.join(ROOT, dependency.source),
'utf8',
Expand All @@ -49,8 +73,9 @@ export async function findStaleSnapshotDependencies(): Promise<
`Could not read ${dependency.packageName} version from ${dependency.source}`,
)
}
const versionSnapshots = dependency.versionSnapshots ?? dependency.snapshots
const snapshots = await Promise.all(
dependency.snapshots.map(async snapshot => ({
versionSnapshots.map(async snapshot => ({
path: snapshot,
contents: await fs.readFile(path.join(ROOT, snapshot), 'utf8'),
})),
Expand All @@ -60,10 +85,13 @@ export async function findStaleSnapshotDependencies(): Promise<
.map(snapshot => snapshot.path)
if (staleSnapshots.length) {
return {
dependencyName: name as SnapshotDependencyName,
dependencyName:
name === 'patch' ? 'socket-patch' : (name as SnapshotDependencyName),
packageName: dependency.packageName,
version,
snapshots: staleSnapshots,
snapshots: dependency.versionSnapshots
? dependency.snapshots
: staleSnapshots,
}
}
return undefined
Expand All @@ -78,6 +106,14 @@ export async function findStaleSnapshotDependencies(): Promise<
return stale
}

export function normalizeSnapshotDependencyName(
name: string,
): SnapshotDependencyMapName | undefined {
if (name === 'socket-patch') {return 'patch'}
if (name === 'coana' || name === 'patch') {return name}
return undefined
}

export function snapshotDependencyUpdateHint(
stale: readonly StaleSnapshotDependency[],
): string {
Expand Down
23 changes: 14 additions & 9 deletions scripts/test/snapshots.mts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import process from 'node:process'
import {
SNAPSHOT_DEPENDENCIES,
assertSnapshotDependenciesCurrent,
normalizeSnapshotDependencyName,
} from './snapshot-dependencies.mts'
import { isMainModule } from '../lib/is-main-module.mts'
import { runMain } from '../lib/run-main.mts'
Expand All @@ -31,9 +32,9 @@ export interface SnapshotOptions {
function missingSnapshotDependencyError(): Error {
return new Error(
'Provide a dependency name. Usage: pnpm snapshots:update:dep <name>. ' +
'Current example: pnpm snapshots:update:dep coana. ' +
'Partner names to map: socket-patch, socket-python-cli. ' +
`Currently mapped: ${Object.keys(SNAPSHOT_DEPENDENCIES).join(', ')}.`,
'Examples: pnpm snapshots:update:dep coana, pnpm snapshots:update:dep socket-patch, or pnpm snapshots:update:dep patch. ' +
'Socket Python CLI still needs a snapshot mapping. ' +
`Currently mapped: ${Object.keys(SNAPSHOT_DEPENDENCIES).join(', ')}, socket-patch.`,
)
}

Expand All @@ -49,9 +50,10 @@ export function parseSnapshotArgs(args: readonly string[]): SnapshotOptions {
if (!value?.trim()) {
throw missingSnapshotDependencyError()
}
if (!(value in SNAPSHOT_DEPENDENCIES)) {
const normalizedName = normalizeSnapshotDependencyName(value)
if (!normalizedName) {
throw new Error(
`Unknown snapshot dependency: ${value}; available: ${Object.keys(SNAPSHOT_DEPENDENCIES).join(', ')}`,
`Unknown snapshot dependency: ${value}; available: coana, socket-patch, patch`,
)
}
if (dependencyName) {
Expand All @@ -75,7 +77,8 @@ export function nativeSnapshotArgs(update: boolean): string[] {
export function dependencySnapshotArgs(
dependencyName: SnapshotDependencyName,
): string[] {
const config = SNAPSHOT_DEPENDENCIES[dependencyName]
const config =
SNAPSHOT_DEPENDENCIES[normalizeSnapshotDependencyName(dependencyName)!]
return [
'--build',
'test/native/commands/cli.native.test.mts',
Expand Down Expand Up @@ -165,6 +168,7 @@ export interface SnapshotWorkflowDependencies {
acquireLock(): Promise<void>
releaseLock(): Promise<void>
createArtifactsDir(): Promise<string>
assertDependenciesCurrent(): Promise<void>
runNative(update: boolean, artifactsDir: string): void
runDependencyNative(dependencyName: SnapshotDependencyName, artifactsDir: string): void
runTerminal(update: boolean, artifactsDir: string): void
Expand Down Expand Up @@ -198,6 +202,7 @@ const DEFAULT_DEPENDENCIES: SnapshotWorkflowDependencies = {
),
)
},
assertDependenciesCurrent: assertSnapshotDependenciesCurrent,
runNative(update, artifactsDir) {
run(['scripts/test/native.mts', ...nativeSnapshotArgs(update)], {
env: { ...process.env, SOCKET_CLI_NATIVE_TEST_ARTIFACTS: artifactsDir },
Expand Down Expand Up @@ -246,7 +251,7 @@ export async function runSnapshotWorkflow(
dependencies.runDependencyNative(selectedDependencyName, artifactsDir)
} else {
if (!update) {
await assertSnapshotDependenciesCurrent()
await dependencies.assertDependenciesCurrent()
}
dependencies.runNative(update, artifactsDir)
dependencies.runTerminal(update, artifactsDir)
Expand All @@ -269,13 +274,13 @@ const SCRIPT_META: ScriptMeta = {
describe: 'builds, runs, and reports native terminal snapshot tests',
help: `Usage: pnpm snapshots [--update|-u]
pnpm snapshots:update
pnpm snapshots:update:dep <name>
pnpm snapshots:update:dep <name> (coana, socket-patch, patch)

Builds the native CLI, runs the focused terminal screenshot tests, and writes
a self-contained HTML report into a fresh build/native-test-artifacts folder.
Pass --update or -u to update the native screenshot baselines. Use the dep
command to rebuild and update only snapshots affected by a pinned tool, for
example: pnpm snapshots:update:dep coana.`,
example: pnpm snapshots:update:dep socket-patch.`,
}

if (isMainModule(import.meta.url)) {
Expand Down
9 changes: 9 additions & 0 deletions test/native-snapshots/socket-patch-version.version.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 15 additions & 0 deletions test/native/commands/cli.native.test.mts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { promises as fs } from 'node:fs'
import path from 'node:path'

import { describe, expect, it } from 'vitest'
Expand Down Expand Up @@ -151,6 +152,20 @@ for (const patchCommand of [
}, 60_000)
}

it('captures bundled socket-patch version', async () => {
const packageMetadata = JSON.parse(
await fs.readFile(path.join(process.cwd(), 'package.json'), 'utf8'),
) as { dependencies: { '@socketsecurity/socket-patch': string } }
await captureFlow({
flow: 'socket-patch-version',
args: ['patch', '--version'],
expectedText: `socket-patch ${packageMetadata.dependencies['@socketsecurity/socket-patch']}`,
exitCode: 0,
rows: 12,
checkpointName: 'version',
})
}, 30_000)

describe('native Socket CLI', () => {
it('reports the version embedded in the SEA manifest', async () => {
await captureFlow({
Expand Down
9 changes: 8 additions & 1 deletion test/unit/scripts/test/snapshot-dependencies.test.mts
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
import { describe, expect, it } from 'vitest'

import { snapshotDependencyUpdateHint } from '../../../../scripts/test/snapshot-dependencies.mts'
import {
findStaleSnapshotDependencies,
snapshotDependencyUpdateHint,
} from '../../../../scripts/test/snapshot-dependencies.mts'

describe('snapshot dependency hints', () => {
it('matches pinned Coana and socket-patch versions in their snapshots', async () => {
await expect(findStaleSnapshotDependencies()).resolves.toEqual([])
})

it('lists stale files and the focused refresh command', () => {
expect(
snapshotDependencyUpdateHint([
Expand Down
30 changes: 27 additions & 3 deletions test/unit/scripts/test/snapshots.test.mts
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,19 @@ describe('native snapshot runner', () => {
update: true,
dependencyName: 'coana',
})
expect(parseSnapshotArgs(['--update', '--dep', 'patch'])).toEqual({
update: true,
dependencyName: 'patch',
})
expect(parseSnapshotArgs(['--update', '--dep', 'socket-patch'])).toEqual({
update: true,
dependencyName: 'socket-patch',
})
expect(() => parseSnapshotArgs(['--dep', 'unknown', '--update'])).toThrow(
'Unknown snapshot dependency',
)
expect(() => parseSnapshotArgs(['--update', '--dep'])).toThrow(
'Partner names to map: socket-patch, socket-python-cli',
'pnpm snapshots:update:dep socket-patch',
)
expect(() => parseSnapshotArgs(['--update', '--dep='])).toThrow(
'Provide a dependency name',
Expand All @@ -53,6 +61,18 @@ describe('native snapshot runner', () => {
])
})

it('selects patch help and version snapshots for a socket-patch refresh', () => {
const patchArgs = [
'--build',
'test/native/commands/cli.native.test.mts',
'--testNamePattern',
'captures bundled patch help for (apply|rollback|get|scan|list|remove|setup|repair)|captures bundled socket-patch version',
'--update',
]
expect(dependencySnapshotArgs('socket-patch')).toEqual(patchArgs)
expect(dependencySnapshotArgs('patch')).toEqual(patchArgs)
})

it('builds and runs only the native screenshot suites', () => {
expect(nativeSnapshotArgs(false)).toEqual([
'--build',
Expand Down Expand Up @@ -108,6 +128,7 @@ describe('native snapshot runner', () => {
expect(events).toEqual([
'acquire',
'create-artifacts',
'assert-dependencies-current',
'native:false:/artifacts/run-a',
'release',
])
Expand Down Expand Up @@ -145,14 +166,17 @@ function workflowDependencies(
events.push('create-artifacts')
return '/artifacts/run-a'
},
async assertDependenciesCurrent() {
events.push('assert-dependencies-current')
},
runNative(update, artifactsDir) {
events.push(`native:${update}:${artifactsDir}`)
if (nativeFails) {
throw new Error('native failed')
}
},
runDependencyNative(dependency, artifactsDir) {
events.push(`dependency-native:${dependency}:${artifactsDir}`)
runDependencyNative(dependencyName, artifactsDir) {
events.push(`dependency-native:${dependencyName}:${artifactsDir}`)
},
runTerminal(update, artifactsDir) {
events.push(`terminal:${update}:${artifactsDir}`)
Expand Down
Loading