Skip to content

Commit 9fc07de

Browse files
jfblaaclaude
andauthored
test(manifest): cover the .NET facts producer (#1681)
* test(manifest): cover the .NET facts producer Unit tests for .NET detection, discovery order and coverage, the no-SDK skip, assembly, the per-framework sidecar and NuGet failure classification, plus `run-compat.sh dotnet`: stub-package fixtures run through the built CLI per SDK, each project's tree compared with `dotnet list package --include-transitive`, with fixture assertions for the dev rule, coverage, solution configurations, twins, design-time build inputs and failures. * test(manifest): cover .NET listings, scan-root exclusions and CLI wiring The dotnet compat matrix now checks the restore-free --workspaces and --members listings and scan-root-relative --exclude-paths on every SDK, as the Gradle, Maven and sbt matrices do. Unit tests cover the dotnet tool's invocation (arguments, preview-SDK roll-forward, missing SDK), solution member listing, the fail-closed exit, auto-manifest and the setup wizard. * test(manifest): cover the .NET review fixes - Smoke-check that --include-configs filters the per-framework build context, that a failed restore never reads another project's assets from an earlier restore, and that a project outside the scan root is never excluded. - Unit-test the SDK probe shared under one global.json and the cached solution listings, and follow isRootCovered, typed entry ordering and code-only NuGet classification. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(manifest): follow the .NET facts simplifications Pass the dotnet config whole to convertDotnetToFacts, and check that a recursive run does not list a solution's members for coverage recorded by another tool's builds. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(manifest): simplify the .NET facts tests Table-driven coverage and wizard cases, file-level temp-dir and mock hooks, and small helpers for the repeated isRootCovered, sidecar and auto-manifest calls. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 254e011 commit 9fc07de

74 files changed

Lines changed: 2466 additions & 162 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import { describe, expect } from 'vitest'
2+
3+
import constants, {
4+
FLAG_CONFIG,
5+
FLAG_DRY_RUN,
6+
FLAG_HELP,
7+
} from '../../../src/constants.mts'
8+
import { cmdit, spawnSocketCli } from '../../../test/utils.mts'
9+
10+
describe('socket manifest dotnet', async () => {
11+
const { binCliPath } = constants
12+
13+
cmdit(
14+
['manifest', 'dotnet', FLAG_HELP, FLAG_CONFIG, '{}'],
15+
`should support ${FLAG_HELP}`,
16+
async cmd => {
17+
const { code, stderr, stdout } = await spawnSocketCli(binCliPath, cmd)
18+
expect(stdout).toMatchInlineSnapshot(`
19+
"[beta] Generate Socket facts files from .NET solutions and projects
20+
21+
Usage
22+
$ socket manifest dotnet [options] [CWD=.|FILE]
23+
24+
Options
25+
--bin Location of the dotnet binary to use, default: dotnet on PATH
26+
--dotnet-opts Additional options to pass on to \`dotnet restore\`, e.g. \`-p:Configuration=Release --configfile nuget.config\`. MSBuild properties among them also apply to project evaluation
27+
--exclude-configs Comma-separated glob patterns; target frameworks matching any pattern are skipped (applied after --include-configs)
28+
--exclude-paths List of glob patterns to exclude from manifest/facts generation. Patterns are anchored micromatch globs matched relative to CWD (\`--cwd\` if set): \`tests\` matches only \`<cwd>/tests\`; use \`**/tests\` to match at any depth. Negation patterns (\`!path\`) are not supported. Accepts a comma-separated value or multiple flags. An excluded JVM build module is not analysed as a project of its own, but where a project that is not excluded depends on it, its code is analysed as part of that project. A build module outside CWD cannot be excluded.
29+
--ignore-unresolved Warn on unresolved dependencies instead of failing the run (unresolved deps are not emitted to the facts file)
30+
--include-configs Comma-separated glob patterns matched against target frameworks (case-sensitive; \`*\`, \`?\`, and \`[...]\` wildcards). Only target frameworks matching at least one pattern are read, e.g. \`net8.0\`. A runtime-specific target (\`net8.0/win-x64\`) also matches its framework. Default: every target framework
31+
--verbose Print debug messages
32+
33+
Emits a \`<file>.socket.facts.json\` per solution or project describing its
34+
resolved NuGet dependency graph. Given a directory, it builds every
35+
solution (.sln, .slnx) and project (.csproj, .fsproj, .vbproj) at its top
36+
level, solutions first, skipping projects a solution already covers; given
37+
a file, just that one (a .slnf solution filter too).
38+
39+
Dependencies come from your own \`dotnet restore\` (same SDK, global.json,
40+
nuget.config and credential providers), so a build that restores for you
41+
restores here. PackageReference projects (central package management and
42+
lock files included) and packages.config projects are supported. An
43+
unresolved dependency is a fatal error unless --ignore-unresolved.
44+
45+
Support is beta. Please report issues or give us feedback on what's missing.
46+
47+
Examples
48+
49+
$ socket manifest dotnet .
50+
$ socket manifest dotnet ./App.sln
51+
$ socket manifest dotnet --dotnet-opts="-p:Configuration=Release" ."
52+
`)
53+
expect(`\n ${stderr}`).toMatchInlineSnapshot(`
54+
"
55+
_____ _ _ /---------------
56+
| __|___ ___| |_ ___| |_ | CLI: <redacted>
57+
|__ | * | _| '_| -_| _| | token: <redacted>, org: <redacted>
58+
|_____|___|___|_,_|___|_|.dev | Command: \`socket manifest dotnet\`, cwd: <redacted>"
59+
`)
60+
61+
expect(code, 'explicit help should exit with code 0').toBe(0)
62+
expect(stderr, 'banner includes base command').toContain(
63+
'`socket manifest dotnet`',
64+
)
65+
},
66+
)
67+
68+
cmdit(
69+
['manifest', 'dotnet', FLAG_DRY_RUN, FLAG_CONFIG, '{}'],
70+
'should require args with just dry-run',
71+
async cmd => {
72+
const { code, stderr, stdout } = await spawnSocketCli(binCliPath, cmd)
73+
expect(stdout).toMatchInlineSnapshot(`"[DryRun]: Bailing now"`)
74+
expect(`\n ${stderr}`).toMatchInlineSnapshot(`
75+
"
76+
_____ _ _ /---------------
77+
| __|___ ___| |_ ___| |_ | CLI: <redacted>
78+
|__ | * | _| '_| -_| _| | token: <redacted>, org: <redacted>
79+
|_____|___|___|_,_|___|_|.dev | Command: \`socket manifest dotnet\`, cwd: <redacted>"
80+
`)
81+
82+
expect(code, 'dry-run should exit with code 0 if input ok').toBe(0)
83+
},
84+
)
85+
})

‎src/commands/manifest/detect-manifest-actions.test.mts‎

Lines changed: 61 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,31 @@
1-
import { mkdtempSync, rmSync, writeFileSync } from 'node:fs'
1+
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
22
import os from 'node:os'
33
import path from 'node:path'
44

55
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
66

7-
import { detectManifestActions } from './detect-manifest-actions.mts'
7+
import {
8+
detectManifestActions,
9+
findDotnetEntries,
10+
} from './detect-manifest-actions.mts'
811

912
import type { SocketJson } from '../../utils/socket-json.mts'
1013

11-
function mkTmp(): string {
12-
return mkdtempSync(path.join(os.tmpdir(), 'detect-manifest-bazel-'))
13-
}
14+
let cwd: string
15+
16+
beforeEach(() => {
17+
cwd = mkdtempSync(path.join(os.tmpdir(), 'detect-manifest-'))
18+
})
19+
20+
afterEach(() => {
21+
rmSync(cwd, { recursive: true, force: true })
22+
})
1423

1524
function touch(dir: string, name: string): void {
1625
writeFileSync(path.join(dir, name), '')
1726
}
1827

1928
describe('detectManifestActions — bazel detector', () => {
20-
let cwd: string
21-
22-
beforeEach(() => {
23-
cwd = mkTmp()
24-
})
25-
26-
afterEach(() => {
27-
rmSync(cwd, { recursive: true, force: true })
28-
})
29-
3029
it('skips bazel when defaults.manifest.bazel.disabled is true', async () => {
3130
touch(cwd, 'MODULE.bazel')
3231
const result = await detectManifestActions(
@@ -75,16 +74,6 @@ describe('detectManifestActions — bazel detector', () => {
7574
})
7675

7776
describe('detectManifestActions — gradle detector', () => {
78-
let cwd: string
79-
80-
beforeEach(() => {
81-
cwd = mkTmp()
82-
})
83-
84-
afterEach(() => {
85-
rmSync(cwd, { recursive: true, force: true })
86-
})
87-
8877
it('detects build.gradle', async () => {
8978
touch(cwd, 'build.gradle')
9079
const result = await detectManifestActions(null, cwd)
@@ -140,16 +129,6 @@ describe('detectManifestActions — gradle detector', () => {
140129
})
141130

142131
describe('detectManifestActions — conda detector', () => {
143-
let cwd: string
144-
145-
beforeEach(() => {
146-
cwd = mkTmp()
147-
})
148-
149-
afterEach(() => {
150-
rmSync(cwd, { recursive: true, force: true })
151-
})
152-
153132
it('detects environment.yml and reports it as the conda file', async () => {
154133
touch(cwd, 'environment.yml')
155134
const result = await detectManifestActions(null, cwd)
@@ -193,3 +172,50 @@ describe('detectManifestActions — conda detector', () => {
193172
expect(result.count).toBe(0)
194173
})
195174
})
175+
176+
describe('detectManifestActions — dotnet detector', () => {
177+
it.each([
178+
'App.sln',
179+
'App.slnx',
180+
'App.csproj',
181+
'App.fsproj',
182+
'App.vbproj',
183+
'APP.SLN',
184+
])('detects %s', async name => {
185+
touch(cwd, name)
186+
expect((await detectManifestActions(null, cwd)).dotnet).toBe(true)
187+
})
188+
189+
it('ignores a solution filter, other files and directories named like projects', async () => {
190+
touch(cwd, 'App.slnf')
191+
touch(cwd, 'App.sqlproj')
192+
mkdirSync(path.join(cwd, 'Lib.csproj'))
193+
expect((await detectManifestActions(null, cwd)).dotnet).toBe(false)
194+
})
195+
196+
it('skips dotnet when defaults.manifest.dotnet.disabled is true', async () => {
197+
touch(cwd, 'App.sln')
198+
const sockJson = {
199+
defaults: { manifest: { dotnet: { disabled: true } } },
200+
} as SocketJson
201+
expect((await detectManifestActions(sockJson, cwd)).dotnet).toBe(false)
202+
})
203+
204+
it('lists solutions before projects, each sorted by name', () => {
205+
for (const name of [
206+
'b.csproj',
207+
'A.vbproj',
208+
'Z.sln',
209+
'M.slnx',
210+
'notes.txt',
211+
]) {
212+
touch(cwd, name)
213+
}
214+
expect(findDotnetEntries(cwd)).toEqual([
215+
'M.slnx',
216+
'Z.sln',
217+
'A.vbproj',
218+
'b.csproj',
219+
])
220+
})
221+
})

0 commit comments

Comments
 (0)