refactor(sshd_config): Use analyzer context instead of LSPError

This commit is contained in:
Myzel394 2024-10-03 17:10:57 +02:00
parent 15b732e136
commit e9fa7a651b
No known key found for this signature in database
GPG Key ID: DEC4AAB876F73185
7 changed files with 237 additions and 218 deletions

View File

@ -8,39 +8,45 @@ import (
protocol "github.com/tliron/glsp/protocol_3_16" protocol "github.com/tliron/glsp/protocol_3_16"
) )
type analyzerContext struct {
document *sshdconfig.SSHDDocument
diagnostics []protocol.Diagnostic
}
func Analyze( func Analyze(
d *sshdconfig.SSHDDocument, d *sshdconfig.SSHDDocument,
) []protocol.Diagnostic { ) []protocol.Diagnostic {
errors := analyzeStructureIsValid(d) ctx := &analyzerContext{
document: d,
diagnostics: make([]protocol.Diagnostic, 0),
}
if len(errors) > 0 { analyzeStructureIsValid(ctx)
return common.ErrsToDiagnostics(errors)
if len(ctx.diagnostics) > 0 {
return ctx.diagnostics
} }
i, indexErrors := indexes.CreateIndexes(*d.Config) i, indexErrors := indexes.CreateIndexes(*d.Config)
d.Indexes = i d.Indexes = i
errors = append(errors, indexErrors...) if len(indexErrors) > 0 {
return common.ErrsToDiagnostics(indexErrors)
if len(errors) > 0 {
return common.ErrsToDiagnostics(errors)
} }
includeErrors := analyzeIncludeValues(d) analyzeIncludeValues(ctx)
if len(includeErrors) > 0 { if len(ctx.diagnostics) == 0 {
errors = append(errors, includeErrors...)
} else {
for _, include := range d.Indexes.Includes { for _, include := range d.Indexes.Includes {
for _, value := range include.Values { for _, value := range include.Values {
for _, path := range value.Paths { for _, path := range value.Paths {
_, err := parseFile(string(path)) _, err := parseFile(string(path))
if err != nil { if err != nil {
errors = append(errors, common.LSPError{ ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{
Range: value.LocationRange, Range: value.LocationRange.ToLSPRange(),
Err: err, Message: err.Error(),
}) })
} }
} }
@ -48,11 +54,7 @@ func Analyze(
} }
} }
errors = append(errors, analyzeMatchBlocks(d)...) analyzeMatchBlocks(ctx)
if len(errors) > 0 { return ctx.diagnostics
return common.ErrsToDiagnostics(errors)
}
return nil
} }

View File

@ -12,31 +12,30 @@ import (
"path" "path"
"path/filepath" "path/filepath"
"regexp" "regexp"
protocol "github.com/tliron/glsp/protocol_3_16"
) )
var whitespacePattern = regexp.MustCompile(`\S+`) var whitespacePattern = regexp.MustCompile(`\S+`)
func analyzeIncludeValues( func analyzeIncludeValues(
d *sshdconfig.SSHDDocument, ctx *analyzerContext,
) []common.LSPError { ) {
errs := make([]common.LSPError, 0) for _, include := range ctx.document.Indexes.Includes {
for _, include := range d.Indexes.Includes {
for _, value := range include.Values { for _, value := range include.Values {
validPaths, err := createIncludePaths(value.Value) validPaths, err := createIncludePaths(value.Value)
if err != nil { if err != nil {
errs = append(errs, common.LSPError{ ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{
Range: value.LocationRange, Range: value.LocationRange.ToLSPRange(),
Err: err, Message: err.Error(),
Severity: &common.SeverityError,
}) })
} else { } else {
value.Paths = validPaths value.Paths = validPaths
} }
} }
} }
return errs
} }
func createIncludePaths( func createIncludePaths(

View File

@ -3,27 +3,26 @@ package analyzer
import ( import (
"config-lsp/common" "config-lsp/common"
docvalues "config-lsp/doc-values" docvalues "config-lsp/doc-values"
sshdconfig "config-lsp/handlers/sshd_config"
"config-lsp/handlers/sshd_config/fields" "config-lsp/handlers/sshd_config/fields"
"config-lsp/handlers/sshd_config/match-parser" "config-lsp/handlers/sshd_config/match-parser"
"config-lsp/utils" "config-lsp/utils"
"errors"
"fmt" "fmt"
"strings" "strings"
protocol "github.com/tliron/glsp/protocol_3_16"
) )
func analyzeMatchBlocks( func analyzeMatchBlocks(
d *sshdconfig.SSHDDocument, ctx *analyzerContext,
) []common.LSPError { ) {
errs := make([]common.LSPError, 0) for matchBlock, options := range ctx.document.Indexes.AllOptionsPerName["Match"] {
for matchBlock, options := range d.Indexes.AllOptionsPerName["Match"] {
option := options[0] option := options[0]
// Check if the match block has filled out all fields // Check if the match block has filled out all fields
if matchBlock == nil || matchBlock.MatchValue == nil || len(matchBlock.MatchValue.Entries) == 0 { if matchBlock == nil || matchBlock.MatchValue == nil || len(matchBlock.MatchValue.Entries) == 0 {
errs = append(errs, common.LSPError{ ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{
Range: option.LocationRange, Range: option.ToLSPRange(),
Err: errors.New("A match expression is required"), Message: "A match expression is required",
Severity: &common.SeverityError,
}) })
continue continue
@ -31,46 +30,48 @@ func analyzeMatchBlocks(
for _, entry := range matchBlock.MatchValue.Entries { for _, entry := range matchBlock.MatchValue.Entries {
if entry.Values == nil { if entry.Values == nil {
errs = append(errs, common.LSPError{ ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{
Range: entry.LocationRange, Range: entry.ToLSPRange(),
Err: errors.New(fmt.Sprintf("A value for %s is required", entry.Criteria.Type)), Message: fmt.Sprintf("A value for %s is required", entry.Criteria.Type),
Severity: &common.SeverityError,
}) })
continue continue
} }
errs = append(errs, analyzeMatchValuesContainsPositiveValue(entry.Values)...) analyzeMatchValuesContainsPositiveValue(ctx, entry.Values)
for _, value := range entry.Values.Values { for _, value := range entry.Values.Values {
errs = append(errs, analyzeMatchValueNegation(value)...) analyzeMatchValueNegation(ctx, value)
errs = append(errs, analyzeMatchValueIsValid(value, entry.Criteria.Type)...) analyzeMatchValueIsValid(ctx, value, entry.Criteria.Type)
} }
} }
// Check if match blocks are not empty // Check if match blocks are not empty
if matchBlock.Options.Size() == 0 { if matchBlock.Options.Size() == 0 {
errs = append(errs, common.LSPError{ ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{
Range: option.LocationRange, Range: option.ToLSPRange(),
Err: errors.New("This match block is empty"), Message: "This match block is empty",
Severity: &common.SeverityInformation,
Tags: []protocol.DiagnosticTag{
protocol.DiagnosticTagUnnecessary,
},
}) })
} }
} }
return errs
} }
func analyzeMatchValueNegation( func analyzeMatchValueNegation(
ctx *analyzerContext,
value *matchparser.MatchValue, value *matchparser.MatchValue,
) []common.LSPError { ) {
errs := make([]common.LSPError, 0)
positionsAsList := utils.AllIndexes(value.Value.Raw, "!") positionsAsList := utils.AllIndexes(value.Value.Raw, "!")
positions := utils.SliceToMap(positionsAsList, struct{}{}) positions := utils.SliceToMap(positionsAsList, struct{}{})
delete(positions, 0) delete(positions, 0)
for position := range positions { for position := range positions {
errs = append(errs, common.LSPError{ ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{
Range: common.LocationRange{ Range: common.LocationRange{
Start: common.Location{ Start: common.Location{
Line: value.Start.Line, Line: value.Start.Line,
@ -80,19 +81,19 @@ func analyzeMatchValueNegation(
Line: value.End.Line, Line: value.End.Line,
Character: uint32(position) + value.End.Character, Character: uint32(position) + value.End.Character,
}, },
}, }.ToLSPRange(),
Err: errors.New("The negation operator (!) may only occur at the beginning of a value"), Message: "The negation operator (!) may only occur at the beginning of a value",
Severity: &common.SeverityError,
}) })
} }
return errs
} }
func analyzeMatchValuesContainsPositiveValue( func analyzeMatchValuesContainsPositiveValue(
ctx *analyzerContext,
values *matchparser.MatchValues, values *matchparser.MatchValues,
) []common.LSPError { ) {
if len(values.Values) == 0 { if len(values.Values) == 0 {
return nil return
} }
containsPositive := false containsPositive := false
@ -105,42 +106,34 @@ func analyzeMatchValuesContainsPositiveValue(
} }
if !containsPositive { if !containsPositive {
return []common.LSPError{ ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{
{ Range: values.LocationRange.ToLSPRange(),
Range: values.LocationRange, Message: "At least one positive value is required. A negated match will never produce a positive result by itself",
Err: errors.New("At least one positive value is required. A negated match will never produce a positive result by itself"), Severity: &common.SeverityError,
}, })
}
} }
return nil
} }
func analyzeMatchValueIsValid( func analyzeMatchValueIsValid(
ctx *analyzerContext,
value *matchparser.MatchValue, value *matchparser.MatchValue,
criteria matchparser.MatchCriteriaType, criteria matchparser.MatchCriteriaType,
) []common.LSPError { ) {
errs := make([]common.LSPError, 0)
if value.Value.Raw == "" { if value.Value.Raw == "" {
return errs return
} }
docOption := fields.MatchValueFieldMap[criteria] docOption := fields.MatchValueFieldMap[criteria]
invalidValues := docOption.DeprecatedCheckIsValid(value.Value.Raw) invalidValues := docOption.DeprecatedCheckIsValid(value.Value.Raw)
errs = append( for _, invalidValue := range invalidValues {
errs, err := docvalues.LSPErrorFromInvalidValue(value.Start.Line, *invalidValue)
utils.Map( err.ShiftCharacter(value.Start.Character)
invalidValues,
func(invalidValue *docvalues.InvalidValue) common.LSPError {
err := docvalues.LSPErrorFromInvalidValue(value.Start.Line, *invalidValue)
err.ShiftCharacter(value.Start.Character)
return err ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{
}, Range: err.Range.ToLSPRange(),
)..., Message: err.Err.Error(),
) Severity: &common.SeverityError,
})
return errs }
} }

View File

@ -6,6 +6,8 @@ import (
"config-lsp/handlers/sshd_config/indexes" "config-lsp/handlers/sshd_config/indexes"
"config-lsp/utils" "config-lsp/utils"
"testing" "testing"
protocol "github.com/tliron/glsp/protocol_3_16"
) )
func TestEmptyMatchBlocksMakesErrors( func TestEmptyMatchBlocksMakesErrors(
@ -22,6 +24,42 @@ Match User root
t.Fatalf("Parse error: %v", errors) t.Fatalf("Parse error: %v", errors)
} }
i, errors := indexes.CreateIndexes(*c)
if len(errors) > 0 {
t.Fatalf("Index error: %v", errors)
}
d := &sshdconfig.SSHDDocument{
Config: c,
Indexes: i,
}
ctx := &analyzerContext{
document: d,
diagnostics: make([]protocol.Diagnostic, 0),
}
analyzeMatchBlocks(ctx)
if !(len(ctx.diagnostics) == 1) {
t.Errorf("Expected 1 error, got %v", len(ctx.diagnostics))
}
}
func TestContainsOnlyNegativeValues(
t *testing.T,
) {
input := utils.Dedent(`
PermitRootLogin yes
Match User !root,!admin
`)
c := ast.NewSSHDConfig()
errors := c.Parse(input)
if len(errors) > 0 {
t.Fatalf("Parse error: %v", errors)
}
indexes, errors := indexes.CreateIndexes(*c) indexes, errors := indexes.CreateIndexes(*c)
if len(errors) > 0 { if len(errors) > 0 {
@ -32,33 +70,16 @@ Match User root
Config: c, Config: c,
Indexes: indexes, Indexes: indexes,
} }
ctx := &analyzerContext{
errors = analyzeMatchBlocks(d) document: d,
diagnostics: make([]protocol.Diagnostic, 0),
if !(len(errors) == 1) {
t.Errorf("Expected 1 error, got %v", len(errors))
}
}
func TestContainsOnlyNegativeValues(
t *testing.T,
) {
input := utils.Dedent(`
PermitRootLogin yes
Match User !root,!admin
`)
c := ast.NewSSHDConfig()
errors := c.Parse(input)
if len(errors) > 0 {
t.Fatalf("Parse error: %v", errors)
} }
_, matchBlock := c.FindOption(uint32(1)) _, matchBlock := c.FindOption(uint32(1))
errors = analyzeMatchValuesContainsPositiveValue(matchBlock.MatchValue.Entries[0].Values) analyzeMatchValuesContainsPositiveValue(ctx, matchBlock.MatchValue.Entries[0].Values)
if !(len(errors) == 1) { if !(len(ctx.diagnostics) == 1) {
t.Errorf("Expected 1 error, got %v", len(errors)) t.Errorf("Expected 1 error, got %v", len(ctx.diagnostics))
} }
} }
@ -87,11 +108,15 @@ Match User
Config: c, Config: c,
Indexes: i, Indexes: i,
} }
ctx := &analyzerContext{
document: d,
diagnostics: make([]protocol.Diagnostic, 0),
}
errors = analyzeMatchBlocks(d) analyzeMatchBlocks(ctx)
if !(len(errors) == 1) { if !(len(ctx.diagnostics) == 1) {
t.Errorf("Expected 1 error, got %v", len(errors)) t.Errorf("Expected 1 error, got %v", len(ctx.diagnostics))
} }
} }
@ -120,10 +145,14 @@ Match User
Config: c, Config: c,
Indexes: i, Indexes: i,
} }
ctx := &analyzerContext{
document: d,
diagnostics: make([]protocol.Diagnostic, 0),
}
errors = analyzeMatchBlocks(d) analyzeMatchBlocks(ctx)
if !(len(errors) == 1) { if !(len(ctx.diagnostics) == 1) {
t.Errorf("Expected 1 error, got %v", len(errors)) t.Errorf("Expected 1 error, got %v", len(ctx.diagnostics))
} }
} }

View File

@ -3,119 +3,102 @@ package analyzer
import ( import (
"config-lsp/common" "config-lsp/common"
docvalues "config-lsp/doc-values" docvalues "config-lsp/doc-values"
sshdconfig "config-lsp/handlers/sshd_config"
"config-lsp/handlers/sshd_config/ast" "config-lsp/handlers/sshd_config/ast"
"config-lsp/handlers/sshd_config/fields" "config-lsp/handlers/sshd_config/fields"
"config-lsp/utils"
"errors"
"fmt" "fmt"
protocol "github.com/tliron/glsp/protocol_3_16"
) )
func analyzeStructureIsValid( func analyzeStructureIsValid(
d *sshdconfig.SSHDDocument, ctx *analyzerContext,
) []common.LSPError { ) {
errs := make([]common.LSPError, 0) it := ctx.document.Config.Options.Iterator()
it := d.Config.Options.Iterator()
for it.Next() { for it.Next() {
entry := it.Value().(ast.SSHDEntry) entry := it.Value().(ast.SSHDEntry)
switch entry.(type) { switch entry.(type) {
case *ast.SSHDOption: case *ast.SSHDOption:
errs = append(errs, checkOption(entry.(*ast.SSHDOption), false)...) checkOption(ctx, entry.(*ast.SSHDOption), false)
case *ast.SSHDMatchBlock: case *ast.SSHDMatchBlock:
matchBlock := entry.(*ast.SSHDMatchBlock) matchBlock := entry.(*ast.SSHDMatchBlock)
errs = append(errs, checkMatchBlock(matchBlock)...) checkMatchBlock(ctx, matchBlock)
} }
} }
return errs
} }
func checkOption( func checkOption(
ctx *analyzerContext,
option *ast.SSHDOption, option *ast.SSHDOption,
isInMatchBlock bool, isInMatchBlock bool,
) []common.LSPError { ) {
errs := make([]common.LSPError, 0)
if option.Key == nil { if option.Key == nil {
return errs return
} }
errs = append(errs, checkIsUsingDoubleQuotes(option.Key.Value, option.Key.LocationRange)...) checkIsUsingDoubleQuotes(ctx, option.Key.Value, option.Key.LocationRange)
errs = append(errs, checkQuotesAreClosed(option.Key.Value, option.Key.LocationRange)...) checkQuotesAreClosed(ctx, option.Key.Value, option.Key.LocationRange)
docOption, found := fields.Options[option.Key.Key] docOption, found := fields.Options[option.Key.Key]
if !found { if !found {
errs = append(errs, common.LSPError{ ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{
Range: option.Key.LocationRange, Range: option.Key.ToLSPRange(),
Err: errors.New(fmt.Sprintf("Unknown option: %s", option.Key.Key)), Message: fmt.Sprintf("Unknown option: %s", option.Key.Key),
Severity: &common.SeverityError,
}) })
return errs return
} }
if _, found := fields.MatchAllowedOptions[option.Key.Key]; !found && isInMatchBlock { if _, found := fields.MatchAllowedOptions[option.Key.Key]; !found && isInMatchBlock {
errs = append(errs, common.LSPError{ ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{
Range: option.Key.LocationRange, Range: option.Key.ToLSPRange(),
Err: errors.New(fmt.Sprintf("Option '%s' is not allowed inside Match blocks", option.Key.Key)), Message: fmt.Sprintf("Option '%s' is not allowed inside Match blocks", option.Key.Key),
Severity: &common.SeverityError,
}) })
return errs
} }
if option.OptionValue == nil || option.OptionValue.Value.Value == "" { if option.OptionValue != nil {
errs = append(errs, common.LSPError{ checkIsUsingDoubleQuotes(ctx, option.OptionValue.Value, option.OptionValue.LocationRange)
Range: option.Key.LocationRange, checkQuotesAreClosed(ctx, option.OptionValue.Value, option.OptionValue.LocationRange)
Err: errors.New(fmt.Sprintf("Option '%s' requires a value", option.Key.Key)),
})
} else {
errs = append(errs, checkIsUsingDoubleQuotes(option.OptionValue.Value, option.OptionValue.LocationRange)...)
errs = append(errs, checkQuotesAreClosed(option.OptionValue.Value, option.OptionValue.LocationRange)...)
invalidValues := docOption.DeprecatedCheckIsValid(option.OptionValue.Value.Value) invalidValues := docOption.DeprecatedCheckIsValid(option.OptionValue.Value.Value)
errs = append( for _, invalidValue := range invalidValues {
errs, err := docvalues.LSPErrorFromInvalidValue(option.Start.Line, *invalidValue)
utils.Map( err.ShiftCharacter(option.OptionValue.Start.Character)
invalidValues,
func(invalidValue *docvalues.InvalidValue) common.LSPError {
err := docvalues.LSPErrorFromInvalidValue(option.Start.Line, *invalidValue)
err.ShiftCharacter(option.OptionValue.Start.Character)
return err ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{
}, Range: err.Range.ToLSPRange(),
)..., Message: err.Err.Error(),
) Severity: &common.SeverityError,
})
}
} }
if option.Separator == nil || option.Separator.Value.Value == "" { if option.Separator == nil || option.Separator.Value.Value == "" {
errs = append(errs, common.LSPError{ ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{
Range: option.Key.LocationRange, Range: option.Key.LocationRange.ToLSPRange(),
Err: errors.New(fmt.Sprintf("There should be a separator between an option and its value")), Message: fmt.Sprintf("There should be a separator between an option and its value"),
Severity: &common.SeverityError,
}) })
} else { } else {
errs = append(errs, checkIsUsingDoubleQuotes(option.Separator.Value, option.Separator.LocationRange)...) checkIsUsingDoubleQuotes(ctx, option.Separator.Value, option.Separator.LocationRange)
errs = append(errs, checkQuotesAreClosed(option.Separator.Value, option.Separator.LocationRange)...) checkQuotesAreClosed(ctx, option.Separator.Value, option.Separator.LocationRange)
} }
return errs
} }
func checkMatchBlock( func checkMatchBlock(
ctx *analyzerContext,
matchBlock *ast.SSHDMatchBlock, matchBlock *ast.SSHDMatchBlock,
) []common.LSPError { ) {
errs := make([]common.LSPError, 0)
it := matchBlock.Options.Iterator() it := matchBlock.Options.Iterator()
for it.Next() { for it.Next() {
option := it.Value().(*ast.SSHDOption) option := it.Value().(*ast.SSHDOption)
errs = append(errs, checkOption(option, true)...) checkOption(ctx, option, true)
} }
return errs
} }

View File

@ -3,57 +3,52 @@ package analyzer
import ( import (
"config-lsp/common" "config-lsp/common"
commonparser "config-lsp/common/parser" commonparser "config-lsp/common/parser"
sshdconfig "config-lsp/handlers/sshd_config" "config-lsp/utils"
"errors"
"strings" "strings"
protocol "github.com/tliron/glsp/protocol_3_16"
) )
func analyzeQuotesAreValid( func analyzeQuotesAreValid(
d *sshdconfig.SSHDDocument, ctx *analyzerContext,
) []common.LSPError { ) {
errs := make([]common.LSPError, 0) for _, option := range ctx.document.Config.GetAllOptions() {
checkIsUsingDoubleQuotes(ctx, option.Key.Value, option.Key.LocationRange)
checkIsUsingDoubleQuotes(ctx, option.OptionValue.Value, option.OptionValue.LocationRange)
for _, option := range d.Config.GetAllOptions() { checkQuotesAreClosed(ctx, option.Key.Value, option.Key.LocationRange)
errs = append(errs, checkIsUsingDoubleQuotes(option.Key.Value, option.Key.LocationRange)...) checkQuotesAreClosed(ctx, option.OptionValue.Value, option.OptionValue.LocationRange)
errs = append(errs, checkIsUsingDoubleQuotes(option.OptionValue.Value, option.OptionValue.LocationRange)...)
errs = append(errs, checkQuotesAreClosed(option.Key.Value, option.Key.LocationRange)...)
errs = append(errs, checkQuotesAreClosed(option.OptionValue.Value, option.OptionValue.LocationRange)...)
} }
return errs
} }
func checkIsUsingDoubleQuotes( func checkIsUsingDoubleQuotes(
ctx *analyzerContext,
value commonparser.ParsedString, value commonparser.ParsedString,
valueRange common.LocationRange, valueRange common.LocationRange,
) []common.LSPError { ) {
quoteRanges := utils.GetQuoteRanges(value.Raw)
singleQuotePosition := strings.Index(value.Raw, "'") singleQuotePosition := strings.Index(value.Raw, "'")
if singleQuotePosition != -1 { // Single quote
return []common.LSPError{ if singleQuotePosition != -1 && !quoteRanges.IsCharInside(singleQuotePosition) {
{ ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{
Range: valueRange, Range: valueRange.ToLSPRange(),
Err: errors.New("sshd_config does not support single quotes. Use double quotes (\") instead."), Message: "sshd_config does not support single quotes. Use double quotes (\") instead.",
}, Severity: &common.SeverityError,
} })
} }
return nil
} }
func checkQuotesAreClosed( func checkQuotesAreClosed(
ctx *analyzerContext,
value commonparser.ParsedString, value commonparser.ParsedString,
valueRange common.LocationRange, valueRange common.LocationRange,
) []common.LSPError { ) {
if strings.Count(value.Raw, "\"")%2 != 0 { if strings.Count(value.Raw, "\"")%2 != 0 {
return []common.LSPError{ ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{
{ Range: valueRange.ToLSPRange(),
Range: valueRange, Message: "There are unclosed quotes here. Make sure all quotes are closed.",
Err: errors.New("There are unclosed quotes here. Make sure all quotes are closed."), Severity: &common.SeverityError,
}, })
}
} }
return nil
} }

View File

@ -3,6 +3,8 @@ package analyzer
import ( import (
testutils_test "config-lsp/handlers/sshd_config/test_utils" testutils_test "config-lsp/handlers/sshd_config/test_utils"
"testing" "testing"
protocol "github.com/tliron/glsp/protocol_3_16"
) )
func TestSimpleInvalidQuotesExample( func TestSimpleInvalidQuotesExample(
@ -11,11 +13,15 @@ func TestSimpleInvalidQuotesExample(
d := testutils_test.DocumentFromInput(t, ` d := testutils_test.DocumentFromInput(t, `
PermitRootLogin 'yes' PermitRootLogin 'yes'
`) `)
ctx := &analyzerContext{
document: d,
diagnostics: make([]protocol.Diagnostic, 0),
}
errors := analyzeQuotesAreValid(d) analyzeQuotesAreValid(ctx)
if !(len(errors) == 1) { if !(len(ctx.diagnostics) == 1) {
t.Errorf("Expected 1 error, got %v", len(errors)) t.Errorf("Expected 1 error, got %v", len(ctx.diagnostics))
} }
} }
@ -25,11 +31,15 @@ func TestSingleQuotesKeyAndOptionExample(
d := testutils_test.DocumentFromInput(t, ` d := testutils_test.DocumentFromInput(t, `
'Port' '22' 'Port' '22'
`) `)
ctx := &analyzerContext{
document: d,
diagnostics: make([]protocol.Diagnostic, 0),
}
errors := analyzeQuotesAreValid(d) analyzeQuotesAreValid(ctx)
if !(len(errors) == 2) { if !(len(ctx.diagnostics) == 2) {
t.Errorf("Expected 2 errors, got %v", len(errors)) t.Errorf("Expected 2 errors, got %v", len(ctx.diagnostics))
} }
} }
@ -39,11 +49,15 @@ func TestSimpleUnclosedQuoteExample(
d := testutils_test.DocumentFromInput(t, ` d := testutils_test.DocumentFromInput(t, `
PermitRootLogin "yes PermitRootLogin "yes
`) `)
ctx := &analyzerContext{
document: d,
diagnostics: make([]protocol.Diagnostic, 0),
}
errors := analyzeQuotesAreValid(d) analyzeQuotesAreValid(ctx)
if !(len(errors) == 1) { if !(len(ctx.diagnostics) == 1) {
t.Errorf("Expected 1 error, got %v", len(errors)) t.Errorf("Expected 1 error, got %v", len(ctx.diagnostics))
} }
} }
@ -53,10 +67,14 @@ func TestIncompleteQuotesExample(
d := testutils_test.DocumentFromInput(t, ` d := testutils_test.DocumentFromInput(t, `
"Port "Port
`) `)
ctx := &analyzerContext{
document: d,
diagnostics: make([]protocol.Diagnostic, 0),
}
errors := analyzeQuotesAreValid(d) analyzeQuotesAreValid(ctx)
if !(len(errors) == 1) { if !(len(ctx.diagnostics) == 1) {
t.Errorf("Expected 1 error, got %v", len(errors)) t.Errorf("Expected 1 error, got %v", len(ctx.diagnostics))
} }
} }