a06f331eb8
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
128 lines
5.1 KiB
Go
128 lines
5.1 KiB
Go
package lsp
|
|
|
|
import (
|
|
"encoding/json"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
// TestIsNuGetAdvisoryCode pins the advisory-code predicate: it must match the
|
|
// NU#### NuGet family (the audit/restore advisories we filter) and must NOT
|
|
// match real CS#### compiler codes — filtering those would hide genuine errors.
|
|
func TestIsNuGetAdvisoryCode(t *testing.T) {
|
|
cases := []struct {
|
|
code string
|
|
want bool
|
|
}{
|
|
{"NU1902", true}, // the transitive-vuln advisory that bites csharp-ls
|
|
{"NU1903", true},
|
|
{"NU1605", true},
|
|
{"NU1", true},
|
|
{"nu1902", true}, // prefix is case-insensitive
|
|
{"CS0246", false}, // real compiler error — must survive the filter
|
|
{"CS1591", false},
|
|
{"NU", false}, // no digits
|
|
{"NUGET", false}, // letters after NU
|
|
{"NU19A2", false}, // non-digit inside the number
|
|
{"1902", false},
|
|
{"N", false},
|
|
{"", false},
|
|
}
|
|
for _, c := range cases {
|
|
assert.Equalf(t, c.want, isNuGetAdvisoryCode(c.code), "isNuGetAdvisoryCode(%q)", c.code)
|
|
}
|
|
}
|
|
|
|
// TestDiagCodeString covers the wire-type normalisation: string codes pass
|
|
// through, json.Number renders, and anything else (incl. JSON-unmarshalled
|
|
// numeric float64) yields "" — sufficient because NU codes are always strings.
|
|
func TestDiagCodeString(t *testing.T) {
|
|
assert.Equal(t, "NU1902", diagCodeString("NU1902"))
|
|
assert.Equal(t, "1902", diagCodeString(json.Number("1902")))
|
|
assert.Equal(t, "", diagCodeString(float64(1902)))
|
|
assert.Equal(t, "", diagCodeString(1902))
|
|
assert.Equal(t, "", diagCodeString(nil))
|
|
}
|
|
|
|
// TestFilterCSharpAdvisoryDiags verifies the filter drops only NU#### NuGet
|
|
// advisories, preserves real CS#### diagnostics and their order, and returns
|
|
// the input untouched when there is nothing to drop.
|
|
func TestFilterCSharpAdvisoryDiags(t *testing.T) {
|
|
// No advisories → same slice handed back, unchanged.
|
|
clean := []Diagnostic{
|
|
{Code: "CS0246", Severity: DiagSeverityError, Message: "type or namespace not found"},
|
|
{Code: "CS1591", Severity: DiagSeverityWarning, Message: "missing XML comment"},
|
|
}
|
|
assert.Equal(t, clean, filterCSharpAdvisoryDiags(clean))
|
|
|
|
// Mixed: NU advisories dropped, CS diagnostics kept in original order.
|
|
mixed := []Diagnostic{
|
|
{Code: "NU1902", Severity: DiagSeverityError, Message: "package has a known vulnerability"},
|
|
{Code: "CS0246", Severity: DiagSeverityError, Message: "type or namespace not found"},
|
|
{Code: "NU1903", Severity: DiagSeverityWarning, Message: "high severity vulnerability"},
|
|
{Code: "CS0103", Severity: DiagSeverityError, Message: "name does not exist"},
|
|
}
|
|
got := filterCSharpAdvisoryDiags(mixed)
|
|
assert.Len(t, got, 2)
|
|
assert.Equal(t, "CS0246", got[0].Code)
|
|
assert.Equal(t, "CS0103", got[1].Code)
|
|
|
|
// All advisories → empty result.
|
|
assert.Empty(t, filterCSharpAdvisoryDiags([]Diagnostic{{Code: "NU1902"}, {Code: "NU1605"}}))
|
|
|
|
// Empty / nil input is safe.
|
|
assert.Empty(t, filterCSharpAdvisoryDiags(nil))
|
|
}
|
|
|
|
// TestServesCSharp checks the language-scoping guard used to confine the
|
|
// C#-specific behaviour to C# providers.
|
|
func TestServesCSharp(t *testing.T) {
|
|
assert.True(t, (&Provider{languages: []string{"csharp"}}).servesCSharp())
|
|
assert.True(t, (&Provider{languages: []string{"go", "csharp"}}).servesCSharp())
|
|
assert.False(t, (&Provider{languages: []string{"go"}}).servesCSharp())
|
|
assert.False(t, (&Provider{}).servesCSharp())
|
|
}
|
|
|
|
// TestCSharpDiagFilterEnabled confirms the filter is ON by default and only
|
|
// the explicit falsey values disable it.
|
|
func TestCSharpDiagFilterEnabled(t *testing.T) {
|
|
t.Setenv(CSharpDiagFilterEnv, "") // unset-equivalent → default ON
|
|
assert.True(t, csharpDiagFilterEnabled())
|
|
|
|
for _, off := range []string{"0", "off", "false", "none", "OFF", "False"} {
|
|
t.Setenv(CSharpDiagFilterEnv, off)
|
|
assert.Falsef(t, csharpDiagFilterEnabled(), "value %q should disable the filter", off)
|
|
}
|
|
|
|
t.Setenv(CSharpDiagFilterEnv, "1")
|
|
assert.True(t, csharpDiagFilterEnabled())
|
|
}
|
|
|
|
// TestCSharpPreRestoreEligible verifies the pre-restore gate: ON by default for
|
|
// a C# provider that is spawning, disabled only by an explicit falsey
|
|
// CSharpRestoreEnv, and never active for a non-C# provider or a passive attach
|
|
// (where the IDE owns restore).
|
|
func TestCSharpPreRestoreEligible(t *testing.T) {
|
|
csharp := func() *Provider { return &Provider{languages: []string{"csharp"}} }
|
|
|
|
t.Setenv(CSharpRestoreEnv, "") // unset-equivalent → default ON
|
|
assert.True(t, csharp().csharpPreRestoreEligible(), "on by default for a spawning C# provider")
|
|
|
|
for _, off := range []string{"0", "off", "false", "none", "OFF", "False"} {
|
|
t.Setenv(CSharpRestoreEnv, off)
|
|
assert.Falsef(t, csharp().csharpPreRestoreEligible(), "value %q should disable pre-restore", off)
|
|
}
|
|
|
|
t.Setenv(CSharpRestoreEnv, "1")
|
|
assert.True(t, csharp().csharpPreRestoreEligible(), "explicitly enabled + serves C# + spawning")
|
|
|
|
// Not a C# provider → never restore, even with restore enabled.
|
|
assert.False(t, (&Provider{languages: []string{"go"}}).csharpPreRestoreEligible())
|
|
|
|
// C# but passively attached (the IDE owns restore) → skip.
|
|
p := csharp()
|
|
p.connect = &ConnectSpec{}
|
|
assert.False(t, p.csharpPreRestoreEligible(), "passive-attach must not trigger restore")
|
|
}
|