test(outdated): add regression test for minimumReleaseAge (#11699)

Adds a regression test for #11698. The underlying fix already shipped as part of #11705 (which removed `strictPublishedByCheck` entirely and routed maturity decisions through `policyViolation`), so this PR now lands only the dedicated test that locks in the behavior.

## What the test covers

`deps/inspection/commands/test/outdated/minimumReleaseAge.test.ts`:

- **Baseline** — without an age policy, `pnpm outdated` reports `is-negative@2.1.0` as an available upgrade (sanity check that the fixture actually has outdated deps).
- **Regression** — with `minimumReleaseAge` set to a cutoff so far in the past that every published version is immature, `pnpm outdated` reports nothing: `exitCode === 0` and `2.1.0` does not appear. Before #11705 this test went red because the non-strict resolver fallback re-picked the immature `latest` ignoring `publishedBy`.

The `allImmatureMinimumReleaseAge = Date.now() / (60 * 1000)` trick (cutoff = epoch in minutes) is date-independent and matches the technique already used in the install-side `minimumReleaseAge` suite.

## Why a test-only PR

The original PR proposed flipping `strictPublishedByCheck` in `createManifestGetter`, but #11705 deleted that option entirely and replaced it with an always-defer model (`policyViolation` flows through `ResolveResult` → `getManifest` returns `null` on `MINIMUM_RELEASE_AGE_VIOLATION`). The test was the durable contribution; preserving it as a regression gate is worth keeping.
This commit is contained in:
Tim Haines
2026-05-20 08:02:49 +09:00
committed by GitHub
parent 3687b0e180
commit 04a2c9c610

View File

@@ -0,0 +1,74 @@
/// <reference path="../../../../../__typings__/index.d.ts" />
import fs from 'node:fs'
import path from 'node:path'
import { stripVTControlCharacters as stripAnsi } from 'node:util'
import { expect, test } from '@jest/globals'
import { outdated } from '@pnpm/deps.inspection.commands'
import { tempDir } from '@pnpm/prepare'
import { REGISTRY_MOCK_PORT } from '@pnpm/registry-mock'
import { fixtures } from '@pnpm/test-fixtures'
const f = fixtures(import.meta.dirname)
const hasOutdatedDepsFixture = f.find('has-outdated-deps')
const REGISTRY_URL = `http://localhost:${REGISTRY_MOCK_PORT}`
const OUTDATED_OPTIONS = {
cacheDir: 'cache',
fetchRetries: 1,
fetchRetryFactor: 1,
fetchRetryMaxtimeout: 60,
fetchRetryMintimeout: 10,
global: false,
networkConcurrency: 16,
offline: false,
configByUri: {},
registries: { default: REGISTRY_URL },
strictSsl: false,
tag: 'latest',
userAgent: '',
userConfig: {},
}
function loadHasOutdatedDeps (): void {
tempDir()
fs.mkdirSync(path.resolve('node_modules/.pnpm'), { recursive: true })
fs.copyFileSync(path.join(hasOutdatedDepsFixture, 'node_modules/.pnpm/lock.yaml'), path.resolve('node_modules/.pnpm/lock.yaml'))
fs.copyFileSync(path.join(hasOutdatedDepsFixture, 'package.json'), path.resolve('package.json'))
}
// A cutoff so far in the past that EVERY published version is "too new" to be
// mature — the same technique as the install-side minimumReleaseAge suite
// (allImmatureMinimumReleaseAge). Date-independent, so it does not depend on
// any registry-mock package's historical publish timestamps.
const allImmatureMinimumReleaseAge = Date.now() / (60 * 1000)
test('pnpm outdated baseline (no minimumReleaseAge): newer versions are offered', async () => {
loadHasOutdatedDeps()
const { output, exitCode } = await outdated.handler({
...OUTDATED_OPTIONS,
dir: process.cwd(),
})
// Sanity: without an age policy, outdated offers the newest registry versions.
expect(exitCode).toBe(1)
expect(stripAnsi(output)).toContain('is-negative')
expect(stripAnsi(output)).toContain('2.1.0')
})
test('pnpm outdated honors minimumReleaseAge: immature newer versions are not offered', async () => {
loadHasOutdatedDeps()
const { output, exitCode } = await outdated.handler({
...OUTDATED_OPTIONS,
dir: process.cwd(),
minimumReleaseAge: allImmatureMinimumReleaseAge,
})
// With every version immature, no upgrade target is mature, so outdated has
// nothing to report — exitCode 0 and 2.1.0 must not appear as available.
expect(exitCode).toBe(0)
expect(stripAnsi(output)).not.toContain('2.1.0')
})