diff --git a/.clj-kondo/.cache/v1/lock b/.clj-kondo/.cache/v1/lock new file mode 100644 index 00000000..e69de29b diff --git a/.lsp/.cache/db.transit.json b/.lsp/.cache/db.transit.json new file mode 100644 index 00000000..3fa699b8 --- /dev/null +++ b/.lsp/.cache/db.transit.json @@ -0,0 +1 @@ +["^ ","~:classpath",["~#set",[]],"~:project-hash","","~:project-root","/Users/anderseknert/git/styra/regal","~:kondo-config-hash","e98a28fd523b559ab8f61e74e2bf5f40a28efd98f0faf95735b82e5adb0607ab","~:dependency-scheme","jar","~:analysis",null,"~:analysis-checksums",["^ "],"~:project-analysis-type","~:project-and-full-dependencies","~:version",12,"~:stubs-generation-namespaces",["^1",[]]] \ No newline at end of file diff --git a/cmd/new.go b/cmd/new.go index 81d70c22..24318d4c 100644 --- a/cmd/new.go +++ b/cmd/new.go @@ -308,7 +308,6 @@ func scaffoldBuiltinRule(params newRuleCommandParams) error { func createBuiltinDocs(params newRuleCommandParams) error { docsDir := filepath.Join(params.output, "docs", "rules", params.category) - docTmpl := template.New("builtin.md.tpl") docTmpl = docTmpl.Funcs(template.FuncMap{ diff --git a/e2e/cli_test.go b/e2e/cli_test.go index 62982a1a..eff622b5 100644 --- a/e2e/cli_test.go +++ b/e2e/cli_test.go @@ -31,15 +31,10 @@ func readProvidedConfig(t *testing.T) config.Config { t.Helper() cwd := testutil.Must(os.Getwd())(t) - - configPath := filepath.Join(cwd, "..", "bundle", "regal", "config", "provided", "data.yaml") - bs, err := os.ReadFile(configPath) - if err != nil { - t.Fatalf("failed to read config from %q: %v", configPath, err) - } + bs := testutil.MustReadFile(t, filepath.Join(cwd, "..", "bundle", "regal", "config", "provided", "data.yaml")) var cfg config.Config - if err = yaml.Unmarshal(bs, &cfg); err != nil { + if err := yaml.Unmarshal(bs, &cfg); err != nil { t.Fatalf("failed to unmarshal config: %v", err) } @@ -855,7 +850,6 @@ func TestFix(t *testing.T) { t.Parallel() stdout, stderr := bytes.Buffer{}, bytes.Buffer{} - td := t.TempDir() initialState := map[string]string{ ".regal/config.yaml": ` @@ -909,9 +903,7 @@ allow if { true } "unrelated.txt": `foobar`, } - for file, content := range initialState { - mustWriteToFile(t, filepath.Join(td, file), content) - } + td := testutil.TempDirectoryOf(t, initialState) // --force is required to make the changes when there is no git repo err := regal(&stdout, &stderr)( @@ -1026,7 +1018,6 @@ func TestFixWithConflicts(t *testing.T) { stdout := bytes.Buffer{} stderr := bytes.Buffer{} - td := t.TempDir() initialState := map[string]string{ ".regal/config.yaml": "", // needed to find the root in the right place @@ -1055,9 +1046,7 @@ import rego.v1 `, } - for file, content := range initialState { - mustWriteToFile(t, filepath.Join(td, file), content) - } + td := testutil.TempDirectoryOf(t, initialState) // --force is required to make the changes when there is no git repo err := regal(&stdout, &stderr)("fix", "--force", td) @@ -1083,7 +1072,7 @@ Cannot move multiple files to: bar/bar.rego } for file, expectedContent := range initialState { - bs := testutil.Must(os.ReadFile(filepath.Join(td, file)))(t) + bs := testutil.MustReadFile(t, filepath.Join(td, file)) if act := string(bs); expectedContent != act { t.Errorf("expected %s contents:\n%s\ngot\n%s", file, expectedContent, act) @@ -1096,7 +1085,6 @@ func TestFixWithConflictRenaming(t *testing.T) { stdout := bytes.Buffer{} stderr := bytes.Buffer{} - td := t.TempDir() initialState := map[string]string{ ".regal/config.yaml": "", // needed to find the root in the right place @@ -1127,9 +1115,7 @@ import rego.v1 `, } - for file, content := range initialState { - mustWriteToFile(t, filepath.Join(td, file), content) - } + td := testutil.TempDirectoryOf(t, initialState) // --force is required to make the changes when there is no git repo // --conflict=rename will rename inbound files when there is a conflict @@ -1205,7 +1191,6 @@ func TestFixSingleFileNested(t *testing.T) { t.Parallel() stdout, stderr := bytes.Buffer{}, bytes.Buffer{} - td := t.TempDir() initialState := map[string]string{ ".regal/config.yaml": ` @@ -1223,15 +1208,13 @@ rules: "foo/foo.rego": `package wow`, } - for file, content := range initialState { - mustWriteToFile(t, filepath.Join(td, file), content) - } + td := testutil.TempDirectoryOf(t, initialState) // --force is required to make the changes when there is no git repo err := regal(&stdout, &stderr)( "fix", "--force", - filepath.Join(td, "foo/foo.rego"), + filepath.Join(td, "foo", "foo.rego"), ) // 0 exit status is expected as all violations should have been fixed @@ -1369,15 +1352,3 @@ func expectExitCode(t *testing.T, err error, exp int, stdout *bytes.Buffer, stde exp, act, stdout.String(), stderr.String()) } } - -func mustWriteToFile(t *testing.T, path string, content string) { - t.Helper() - - if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil { - t.Fatalf("failed to create directory %s: %v", filepath.Dir(path), err) - } - - if err := os.WriteFile(path, []byte(content), 0o644); err != nil { - t.Fatalf("failed to write to %s: %v", path, err) - } -} diff --git a/internal/lsp/bundles/bundles_test.go b/internal/lsp/bundles/bundles_test.go index 2f0d76d2..3e7d0ccc 100644 --- a/internal/lsp/bundles/bundles_test.go +++ b/internal/lsp/bundles/bundles_test.go @@ -1,10 +1,11 @@ package bundles import ( - "os" "path/filepath" "reflect" "testing" + + "github.com/styrainc/regal/internal/testutil" ) func TestLoadDataBundle(t *testing.T) { @@ -67,21 +68,7 @@ func TestLoadDataBundle(t *testing.T) { t.Run(testCase, func(t *testing.T) { t.Parallel() - workspacePath := t.TempDir() - - // create the workspace state - for file, contents := range testData.files { - filePath := filepath.Join(workspacePath, file) - - dir := filepath.Dir(filePath) - if err := os.MkdirAll(dir, 0o755); err != nil { - t.Fatalf("failed to create directory %s: %v", dir, err) - } - - if err := os.WriteFile(filePath, []byte(contents), 0o600); err != nil { - t.Fatalf("failed to write file %s: %v", filePath, err) - } - } + workspacePath := testutil.TempDirectoryOf(t, testData.files) b, err := LoadDataBundle(filepath.Join(workspacePath, testData.path)) if err != nil { diff --git a/internal/lsp/bundles/cache_test.go b/internal/lsp/bundles/cache_test.go index 01a87e39..649f6dbd 100644 --- a/internal/lsp/bundles/cache_test.go +++ b/internal/lsp/bundles/cache_test.go @@ -6,35 +6,17 @@ import ( "reflect" "slices" "testing" + + "github.com/styrainc/regal/internal/testutil" ) func TestRefresh(t *testing.T) { t.Parallel() - workspacePath := t.TempDir() - - // create the initial filesystem state - files := map[string]string{ + workspacePath := testutil.TempDirectoryOf(t, map[string]string{ "foo/.manifest": `{"roots":["foo"]}`, "foo/data.json": `{"foo": "bar"}`, - } - - writeFiles := func(files map[string]string) { - for file, contents := range files { - filePath := filepath.Join(workspacePath, file) - - dir := filepath.Dir(filePath) - if err := os.MkdirAll(dir, 0o755); err != nil { - t.Fatalf("failed to create directory %s: %v", dir, err) - } - - if err := os.WriteFile(filePath, []byte(contents), 0o600); err != nil { - t.Fatalf("failed to write file %s: %v", filePath, err) - } - } - } - - writeFiles(files) + }) c := NewCache(&CacheOptions{WorkspacePath: workspacePath}) @@ -80,11 +62,7 @@ func TestRefresh(t *testing.T) { } // add a new unrelated file - writeFiles( - map[string]string{ - "foo/foo.rego": `package wow`, - }, - ) + testutil.MustWriteFile(t, filepath.Join(workspacePath, "foo", "foo.rego"), []byte(`package wow`)) // perform the third load of the bundles, after adding a new unrelated file refreshedBundles, err = c.Refresh() @@ -97,11 +75,7 @@ func TestRefresh(t *testing.T) { } // update the data in the bundle - writeFiles( - map[string]string{ - "foo/data.json": `{"foo": "baz"}`, - }, - ) + testutil.MustWriteFile(t, filepath.Join(workspacePath, "foo", "data.json"), []byte(`{"foo": "baz"}`)) refreshedBundles, err = c.Refresh() if err != nil { @@ -122,12 +96,9 @@ func TestRefresh(t *testing.T) { } // create a new bundle - writeFiles( - map[string]string{ - "bar/.manifest": `{"roots":["bar"]}`, - "bar/data.json": `{"bar": true}`, - }, - ) + testutil.MustMkdirAll(t, workspacePath, "bar") + testutil.MustWriteFile(t, filepath.Join(workspacePath, "bar", ".manifest"), []byte(`{"roots":["bar"]}`)) + testutil.MustWriteFile(t, filepath.Join(workspacePath, "bar", "data.json"), []byte(`{"bar": true}`)) refreshedBundles, err = c.Refresh() if err != nil { diff --git a/internal/lsp/config/watcher_test.go b/internal/lsp/config/watcher_test.go index 4790317f..589334bf 100644 --- a/internal/lsp/config/watcher_test.go +++ b/internal/lsp/config/watcher_test.go @@ -3,26 +3,22 @@ package config import ( "context" "os" + "path/filepath" "testing" "time" "github.com/styrainc/regal/internal/lsp/log" + "github.com/styrainc/regal/internal/testutil" ) func TestWatcher(t *testing.T) { t.Parallel() - tempDir := t.TempDir() - - configFilePath := tempDir + "/config.yaml" - - configFileContents := `--- + tempDir := testutil.TempDirectoryOf(t, map[string]string{ + "config.yaml": `--- foo: bar -` - - if err := os.WriteFile(configFilePath, []byte(configFileContents), 0o600); err != nil { - t.Fatal(err) - } +`, + }) watcher := NewWatcher(&WatcherOpts{LogFunc: func(l log.Level, s string, a ...any) { t.Logf(l.String()+": "+s, a...) @@ -37,6 +33,8 @@ foo: bar } }() + configFilePath := filepath.Join(tempDir, "config.yaml") + watcher.Watch(configFilePath) select { @@ -48,10 +46,7 @@ foo: bar newConfigFileContents := `--- foo: baz ` - - if err := os.WriteFile(configFilePath, []byte(newConfigFileContents), 0o600); err != nil { - t.Fatal(err) - } + testutil.MustWriteFile(t, configFilePath, []byte(newConfigFileContents)) select { case <-watcher.Reload: diff --git a/internal/lsp/eval_test.go b/internal/lsp/eval_test.go index ee990915..d7c1da62 100644 --- a/internal/lsp/eval_test.go +++ b/internal/lsp/eval_test.go @@ -4,12 +4,14 @@ import ( "context" "maps" "os" + "path/filepath" "slices" "testing" rio "github.com/styrainc/regal/internal/io" "github.com/styrainc/regal/internal/lsp/log" "github.com/styrainc/regal/internal/parse" + "github.com/styrainc/regal/internal/testutil" ) func TestEvalWorkspacePath(t *testing.T) { @@ -115,12 +117,10 @@ func TestFindInput(t *testing.T) { tmpDir := t.TempDir() - workspacePath := tmpDir + "/workspace" - file := tmpDir + "/workspace/foo/bar/baz.rego" + workspacePath := filepath.Join(tmpDir, "workspace") + file := filepath.Join(tmpDir, "workspace", "foo", "bar", "baz.rego") - if err := os.MkdirAll(workspacePath+"/foo/bar", 0o755); err != nil { - t.Fatal(err) - } + testutil.MustMkdirAll(t, workspacePath, "foo", "bar") path, content := rio.FindInput(file, workspacePath) if path != "" || content != nil { @@ -134,9 +134,7 @@ func TestFindInput(t *testing.T) { t.Errorf(`expected input {"x": true} at, got %s`, content) } - if err := os.Remove(tmpDir + "/workspace/foo/bar/input." + tc.fileType); err != nil { - t.Fatal(err) - } + testutil.MustRemove(t, tmpDir+"/workspace/foo/bar/input."+tc.fileType) createWithContent(t, tmpDir+"/workspace/input."+tc.fileType, tc.fileContent) @@ -151,14 +149,10 @@ func TestFindInput(t *testing.T) { func createWithContent(t *testing.T, path string, content string) { t.Helper() - f, err := os.Create(path) - if err != nil { - t.Fatal(err) - } - + f := testutil.Must(os.Create(path))(t) defer f.Close() - if _, err = f.WriteString(content); err != nil { + if _, err := f.WriteString(content); err != nil { t.Fatal(err) } } diff --git a/internal/lsp/hover/hover_test.go b/internal/lsp/hover/hover_test.go index 9b5abca9..a6658838 100644 --- a/internal/lsp/hover/hover_test.go +++ b/internal/lsp/hover/hover_test.go @@ -1,11 +1,12 @@ package hover import ( - "os" "testing" "github.com/open-policy-agent/opa/v1/ast" "github.com/open-policy-agent/opa/v1/types" + + "github.com/styrainc/regal/internal/testutil" ) func TestCreateHoverContent(t *testing.T) { @@ -45,11 +46,7 @@ func TestCreateHoverContent(t *testing.T) { } for _, c := range cases { - file, err := os.ReadFile(c.testdata) - if err != nil { - t.Fatal(err) - } - + file := testutil.MustReadFile(t, c.testdata) hoverContent := CreateHoverContent(c.builtin) if string(file) != hoverContent { diff --git a/internal/lsp/server_aggregates_test.go b/internal/lsp/server_aggregates_test.go index 8a41ee03..040321af 100644 --- a/internal/lsp/server_aggregates_test.go +++ b/internal/lsp/server_aggregates_test.go @@ -11,6 +11,7 @@ import ( "github.com/sourcegraph/jsonrpc2" "github.com/styrainc/regal/internal/lsp/types" + "github.com/styrainc/regal/internal/testutil" "github.com/styrainc/regal/pkg/report" ) @@ -36,21 +37,15 @@ import rego.v1 ".regal/config.yaml": ``, } - messages := createMessageChannels(files) - logger := newTestLogger(t) - + tempDir := testutil.TempDirectoryOf(t, files) + messages := createMessageChannels(files) clientHandler := createClientHandler(t, logger, messages) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - tempDir := t.TempDir() - - _, connClient, err := createAndInitServer(ctx, logger, tempDir, files, clientHandler) - if err != nil { - t.Fatalf("failed to create and init language server: %s", err) - } + _, connClient := createAndInitServer(t, ctx, logger, tempDir, clientHandler) timeout := time.NewTimer(determineTimeout()) defer timeout.Stop() @@ -73,7 +68,7 @@ import rego.v1 barURI := fileURIScheme + filepath.Join(tempDir, "bar.rego") - err = connClient.Notify(ctx, "textDocument/didChange", types.TextDocumentDidChangeParams{ + if err := connClient.Notify(ctx, "textDocument/didChange", types.TextDocumentDidChangeParams{ TextDocument: types.TextDocumentIdentifier{ URI: barURI, }, @@ -85,8 +80,7 @@ import rego.v1 `, }, }, - }, nil) - if err != nil { + }, nil); err != nil { t.Fatalf("failed to send didChange notification: %s", err) } @@ -110,7 +104,7 @@ import rego.v1 fooURI := fileURIScheme + filepath.Join(tempDir, "foo.rego") - err = connClient.Notify(ctx, "textDocument/didChange", types.TextDocumentDidChangeParams{ + if err := connClient.Notify(ctx, "textDocument/didChange", types.TextDocumentDidChangeParams{ TextDocument: types.TextDocumentIdentifier{ URI: fooURI, }, @@ -125,8 +119,7 @@ import data.qux # new name for bar.rego package `, }, }, - }, nil) - if err != nil { + }, nil); err != nil { t.Fatalf("failed to send didChange notification: %s", err) } @@ -179,12 +172,9 @@ package bar ctx, cancel := context.WithCancel(context.Background()) defer cancel() - tempDir := t.TempDir() + tempDir := testutil.TempDirectoryOf(t, files) - _, _, err := createAndInitServer(ctx, logger, tempDir, files, clientHandler) - if err != nil { - t.Fatalf("failed to create and init language server: %s", err) - } + createAndInitServer(t, ctx, logger, tempDir, clientHandler) timeout := time.NewTimer(determineTimeout()) defer timeout.Stop() @@ -232,12 +222,8 @@ import data.quz ctx, cancel := context.WithCancel(context.Background()) defer cancel() - tempDir := t.TempDir() - - ls, connClient, err := createAndInitServer(ctx, newTestLogger(t), tempDir, files, clientHandler) - if err != nil { - t.Fatalf("failed to create and init language server: %s", err) - } + tempDir := testutil.TempDirectoryOf(t, files) + ls, connClient := createAndInitServer(t, ctx, newTestLogger(t), tempDir, clientHandler) // 1. check the Aggregates are set at start up timeout := time.NewTimer(determineTimeout()) @@ -303,7 +289,7 @@ import data.quz } // 2. check the aggregates for a file are updated after an update - err = connClient.Notify(ctx, "textDocument/didChange", types.TextDocumentDidChangeParams{ + if err := connClient.Notify(ctx, "textDocument/didChange", types.TextDocumentDidChangeParams{ TextDocument: types.TextDocumentIdentifier{ URI: fileURIScheme + filepath.Join(tempDir, "bar.rego"), }, @@ -318,8 +304,7 @@ import data.wow # new `, }, }, - }, nil) - if err != nil { + }, nil); err != nil { t.Fatalf("failed to send didChange notification: %s", err) } @@ -348,7 +333,6 @@ func TestLanguageServerAggregateViolationFixedAndReintroducedInUnviolatingFileCh var err error - tempDir := t.TempDir() files := map[string]string{ "foo.rego": `package foo @@ -365,20 +349,16 @@ import rego.v1 ".regal/config.yaml": ``, } - messages := createMessageChannels(files) - logger := newTestLogger(t) - + tempDir := testutil.TempDirectoryOf(t, files) + messages := createMessageChannels(files) clientHandler := createClientHandler(t, logger, messages) // set up the server and client connections ctx, cancel := context.WithCancel(context.Background()) defer cancel() - _, connClient, err := createAndInitServer(ctx, logger, tempDir, files, clientHandler) - if err != nil { - t.Fatalf("failed to create and init language server: %s", err) - } + _, connClient := createAndInitServer(t, ctx, logger, tempDir, clientHandler) // wait for foo.rego to have the correct violations timeout := time.NewTimer(determineTimeout()) diff --git a/internal/lsp/server_config_test.go b/internal/lsp/server_config_test.go index 20d8a993..6c1dc547 100644 --- a/internal/lsp/server_config_test.go +++ b/internal/lsp/server_config_test.go @@ -2,7 +2,6 @@ package lsp import ( "context" - "os" "path/filepath" "slices" "testing" @@ -13,6 +12,7 @@ import ( "github.com/styrainc/regal/internal/lsp/types" "github.com/styrainc/regal/internal/lsp/uri" + "github.com/styrainc/regal/internal/testutil" ) // TestLanguageServerParentDirConfig tests that regal config is loaded as it is for the @@ -21,21 +21,14 @@ import ( func TestLanguageServerParentDirConfig(t *testing.T) { t.Parallel() - var err error - - // this is the top level directory for the test - tempDir := t.TempDir() - // childDir will be the directory that the client is using as its workspace - - childDirName := "child" - childDir := filepath.Join(tempDir, childDirName) - mainRegoContents := `package main import data.test allow := true ` + childDirName := "child" + files := map[string]string{ childDirName + mainRegoFileName: mainRegoContents, ".regal/config.yaml": `rules: @@ -48,6 +41,10 @@ allow := true `, } + // childDir will be the directory that the client is using as its workspace + tempDir := testutil.TempDirectoryOf(t, files) + childDir := filepath.Join(tempDir, childDirName) + // mainRegoFileURI is used throughout the test to refer to the main.rego file // and so it is defined here for convenience mainRegoFileURI := fileURIScheme + childDir + mainRegoFileName @@ -75,10 +72,7 @@ allow := true return struct{}{}, nil } - ls, _, err := createAndInitServer(ctx, newTestLogger(t), tempDir, files, clientHandler) - if err != nil { - t.Fatalf("failed to create and init language server: %s", err) - } + ls, _ := createAndInitServer(t, ctx, newTestLogger(t), tempDir, clientHandler) if got, exp := ls.workspaceRootURI, uri.FromPath(ls.clientIdentifier, tempDir); exp != got { t.Fatalf("expected client root URI to be %s, got %s", exp, got) @@ -107,10 +101,7 @@ allow := true level: ignore ` - path := filepath.Join(tempDir, ".regal/config.yaml") - if err := os.WriteFile(path, []byte(newConfigContents), 0o600); err != nil { - t.Fatalf("failed to write new config file: %s", err) - } + testutil.MustWriteFile(t, filepath.Join(tempDir, ".regal", "config.yaml"), []byte(newConfigContents)) // validate that the client received a new, empty diagnostics notification for the file timeout.Reset(determineTimeout()) @@ -128,8 +119,6 @@ allow := true func TestLanguageServerCachesEnabledRulesAndUsesDefaultConfig(t *testing.T) { t.Parallel() - var err error - tempDir := t.TempDir() ctx, cancel := context.WithCancel(context.Background()) @@ -142,10 +131,7 @@ func TestLanguageServerCachesEnabledRulesAndUsesDefaultConfig(t *testing.T) { return struct{}{}, nil } - ls, connClient, err := createAndInitServer(ctx, newTestLogger(t), tempDir, map[string]string{}, clientHandler) - if err != nil { - t.Fatalf("failed to create and init language server: %s", err) - } + ls, connClient := createAndInitServer(t, ctx, newTestLogger(t), tempDir, clientHandler) if got, exp := ls.workspaceRootURI, uri.FromPath(ls.clientIdentifier, tempDir); exp != got { t.Fatalf("expected client root URI to be %s, got %s", exp, got) @@ -172,10 +158,7 @@ func TestLanguageServerCachesEnabledRulesAndUsesDefaultConfig(t *testing.T) { } } - err = os.MkdirAll(filepath.Join(tempDir, ".regal"), 0o755) - if err != nil { - t.Fatalf("failed to create regal config dir: %s", err) - } + testutil.MustMkdirAll(t, tempDir, ".regal") configContents := ` rules: @@ -187,16 +170,13 @@ rules: level: ignore ` - err = os.WriteFile(filepath.Join(tempDir, ".regal/config.yaml"), []byte(configContents), 0o600) - if err != nil { - t.Fatalf("failed to write regal config file: %s", err) - } + testutil.MustWriteFile(t, filepath.Join(tempDir, ".regal", "config.yaml"), []byte(configContents)) // this event is sent to allow the server to detect the new config if err := connClient.Notify(ctx, "workspace/didChangeWatchedFiles", types.WorkspaceDidChangeWatchedFilesParams{ Changes: []types.FileEvent{ { - URI: fileURIScheme + filepath.Join(tempDir, ".regal/config.yaml"), + URI: fileURIScheme + filepath.Join(tempDir, ".regal", "config.yaml"), Type: 1, // created }, }, @@ -243,11 +223,7 @@ rules: level: error ` - err = os.WriteFile(filepath.Join(tempDir, ".regal/config.yaml"), []byte(configContents2), 0o600) - if err != nil { - t.Fatalf("failed to write regal config file: %s", err) - } - + testutil.MustWriteFile(t, filepath.Join(tempDir, ".regal", "config.yaml"), []byte(configContents2)) timeout.Reset(determineTimeout()) for success := false; !success; { diff --git a/internal/lsp/server_formatting_test.go b/internal/lsp/server_formatting_test.go index ae9928a2..6a5fc299 100644 --- a/internal/lsp/server_formatting_test.go +++ b/internal/lsp/server_formatting_test.go @@ -25,12 +25,8 @@ func TestFormatting(t *testing.T) { return struct{}{}, nil } - ls, _, err := createAndInitServer(ctx, newTestLogger(t), tempDir, map[string]string{}, clientHandler) - if err != nil { - t.Fatalf("failed to create and init language server: %s", err) - } - - mainRegoURI := fileURIScheme + filepath.Join(tempDir, "main/main.rego") + ls, _ := createAndInitServer(t, ctx, newTestLogger(t), tempDir, clientHandler) + mainRegoURI := fileURIScheme + filepath.Join(tempDir, "main", "main.rego") // Simple as possible — opa fmt should just remove a newline content := `package main diff --git a/internal/lsp/server_multi_file_test.go b/internal/lsp/server_multi_file_test.go index bffb233c..948e4887 100644 --- a/internal/lsp/server_multi_file_test.go +++ b/internal/lsp/server_multi_file_test.go @@ -8,6 +8,7 @@ import ( "time" "github.com/styrainc/regal/internal/lsp/types" + "github.com/styrainc/regal/internal/testutil" ) // TestLanguageServerMultipleFiles tests that changes to multiple files are handled correctly. When there are multiple @@ -19,9 +20,6 @@ func TestLanguageServerMultipleFiles(t *testing.T) { t.Skip() t.Parallel() - // set up the workspace content with some example rego and regal config - tempDir := t.TempDir() - files := map[string]string{ "authz.rego": `package authz @@ -54,20 +52,18 @@ ignore: `, } - logger := newTestLogger(t) + // set up the workspace content with some example rego and regal config + tempDir := testutil.TempDirectoryOf(t, files) + logger := newTestLogger(t) messages := createMessageChannels(files) - clientHandler := createClientHandler(t, logger, messages) // set up the server and client connections ctx, cancel := context.WithCancel(context.Background()) defer cancel() - ls, connClient, err := createAndInitServer(ctx, logger, tempDir, files, clientHandler) - if err != nil { - t.Fatalf("failed to create and init language server: %s", err) - } + ls, connClient := createAndInitServer(t, ctx, logger, tempDir, clientHandler) timeout := time.NewTimer(determineTimeout()) defer timeout.Stop() diff --git a/internal/lsp/server_rename_test.go b/internal/lsp/server_rename_test.go index fd98bd2f..eab9f410 100644 --- a/internal/lsp/server_rename_test.go +++ b/internal/lsp/server_rename_test.go @@ -3,14 +3,13 @@ package lsp import ( "context" "fmt" - "os" - "path/filepath" "testing" "github.com/styrainc/regal/internal/lsp/cache" "github.com/styrainc/regal/internal/lsp/clients" "github.com/styrainc/regal/internal/lsp/log" "github.com/styrainc/regal/internal/lsp/types" + "github.com/styrainc/regal/internal/testutil" "github.com/styrainc/regal/pkg/config" "github.com/styrainc/regal/pkg/fixer/fixes" ) @@ -20,9 +19,7 @@ func TestLanguageServerFixRenameParams(t *testing.T) { tmpDir := t.TempDir() - if err := os.MkdirAll(filepath.Join(tmpDir, "workspace/foo/bar"), 0o755); err != nil { - t.Fatalf("failed to create directory: %s", err) - } + testutil.MustMkdirAll(t, tmpDir, "workspace", "foo", "bar") ctx := context.Background() @@ -86,15 +83,11 @@ func TestLanguageServerFixRenameParams(t *testing.T) { func TestLanguageServerFixRenameParamsWithConflict(t *testing.T) { t.Parallel() - tmpDir := t.TempDir() - - if err := os.MkdirAll(filepath.Join(tmpDir, "workspace/foo/bar"), 0o755); err != nil { - t.Fatalf("failed to create directory: %s", err) - } - ctx := context.Background() - logger := newTestLogger(t) + tmpDir := t.TempDir() + + testutil.MustMkdirAll(t, tmpDir, "workspace", "foo", "bar") ls := NewLanguageServer( ctx, diff --git a/internal/lsp/server_single_file_test.go b/internal/lsp/server_single_file_test.go index 333e73fe..05d2ebd0 100644 --- a/internal/lsp/server_single_file_test.go +++ b/internal/lsp/server_single_file_test.go @@ -2,7 +2,6 @@ package lsp import ( "context" - "os" "path/filepath" "testing" "time" @@ -11,6 +10,7 @@ import ( "github.com/sourcegraph/jsonrpc2" "github.com/styrainc/regal/internal/lsp/types" + "github.com/styrainc/regal/internal/testutil" ) // TestLanguageServerSingleFile tests that changes to a single file and Regal config are handled correctly by the @@ -23,14 +23,6 @@ import ( func TestLanguageServerSingleFile(t *testing.T) { t.Parallel() - // set up the workspace content with some example rego and regal config - tempDir := t.TempDir() - mainRegoURI := fileURIScheme + tempDir + mainRegoFileName - - if err := os.MkdirAll(filepath.Join(tempDir, ".regal"), 0o755); err != nil { - t.Fatalf("failed to create .regal directory: %s", err) - } - mainRegoContents := `package main import rego.v1 @@ -46,11 +38,9 @@ rules: level: ignore`, } - for f, fc := range files { - if err := os.WriteFile(filepath.Join(tempDir, f), []byte(fc), 0o600); err != nil { - t.Fatalf("failed to write file %s: %s", f, err) - } - } + // set up the workspace content with some example rego and regal config + tempDir := testutil.TempDirectoryOf(t, files) + mainRegoURI := fileURIScheme + tempDir + mainRegoFileName receivedMessages := make(chan types.FileDiagnostics, defaultBufferedChannelSize) clientHandler := func(_ context.Context, _ *jsonrpc2.Conn, req *jsonrpc2.Request) (result any, err error) { @@ -75,10 +65,7 @@ rules: ctx, cancel := context.WithCancel(context.Background()) defer cancel() - _, connClient, err := createAndInitServer(ctx, newTestLogger(t), tempDir, files, clientHandler) - if err != nil { - t.Fatalf("failed to create and init language server: %s", err) - } + _, connClient := createAndInitServer(t, ctx, newTestLogger(t), tempDir, clientHandler) // validate that the client received a diagnostics notification for the file timeout := time.NewTimer(determineTimeout()) @@ -134,9 +121,7 @@ rules: level: ignore ` - if err := os.WriteFile(filepath.Join(tempDir, ".regal/config.yaml"), []byte(newConfigContents), 0o600); err != nil { - t.Fatalf("failed to write new config file: %s", err) - } + testutil.MustWriteFile(t, filepath.Join(tempDir, ".regal", "config.yaml"), []byte(newConfigContents)) // validate that the client received a new, empty diagnostics notification for the file timeout.Reset(determineTimeout()) @@ -182,9 +167,7 @@ capabilities: version: v1.23.0 ` - if err := os.WriteFile(filepath.Join(tempDir, ".regal/config.yaml"), []byte(newConfigContents), 0o600); err != nil { - t.Fatalf("failed to write new config file: %s", err) - } + testutil.MustWriteFile(t, filepath.Join(tempDir, ".regal", "config.yaml"), []byte(newConfigContents)) // validate that the client received a new, empty diagnostics notification for the file timeout.Reset(determineTimeout()) diff --git a/internal/lsp/server_template_test.go b/internal/lsp/server_template_test.go index 441044ca..14bcd888 100644 --- a/internal/lsp/server_template_test.go +++ b/internal/lsp/server_template_test.go @@ -4,7 +4,6 @@ import ( "context" "encoding/json" "fmt" - "os" "path/filepath" "strings" "testing" @@ -18,6 +17,7 @@ import ( "github.com/styrainc/regal/internal/lsp/log" "github.com/styrainc/regal/internal/lsp/types" "github.com/styrainc/regal/internal/lsp/uri" + "github.com/styrainc/regal/internal/testutil" "github.com/styrainc/regal/internal/util/concurrent" ) @@ -104,21 +104,7 @@ func TestTemplateContentsForFile(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() - td := t.TempDir() - - // init the state on disk - for f, c := range tc.DiskContents { - dir := filepath.Dir(f) - - if err := os.MkdirAll(filepath.Join(td, dir), 0o755); err != nil { - t.Fatalf("failed to create directory %s: %s", dir, err) - } - - if err := os.WriteFile(filepath.Join(td, f), []byte(c), 0o600); err != nil { - t.Fatalf("failed to write file %s: %s", f, err) - } - } - + td := testutil.TempDirectoryOf(t, tc.DiskContents) logger := newTestLogger(t) ls := NewLanguageServer( @@ -154,19 +140,11 @@ func TestTemplateContentsForFileInWorkspaceRoot(t *testing.T) { t.Parallel() td := t.TempDir() - - err := os.MkdirAll(filepath.Join(td, ".regal"), 0o755) - if err != nil { - t.Fatalf("failed to create directory %s: %s", filepath.Join(td, ".regal"), err) - } - - err = os.WriteFile(filepath.Join(td, ".regal/config.yaml"), []byte{}, 0o600) - if err != nil { - t.Fatalf("failed to create file %s: %s", filepath.Join(td, ".regal"), err) - } - logger := newTestLogger(t) + testutil.MustMkdirAll(t, td, ".regal") + testutil.MustWriteFile(t, filepath.Join(td, ".regal", "config.yaml"), []byte{}) + ls := NewLanguageServer( context.Background(), &LanguageServerOptions{LogWriter: logger, LogLevel: log.LevelDebug}, @@ -178,12 +156,9 @@ func TestTemplateContentsForFileInWorkspaceRoot(t *testing.T) { ls.cache.SetFileContents(fileURI, "") - _, err = ls.templateContentsForFile(fileURI) - if err == nil { + if _, err := ls.templateContentsForFile(fileURI); err == nil { t.Fatalf("expected error") - } - - if !strings.Contains(err.Error(), "this function does not template files in the workspace root") { + } else if !strings.Contains(err.Error(), "this function does not template files in the workspace root") { t.Fatalf("expected error about root templating, got %s", err.Error()) } } @@ -192,7 +167,6 @@ func TestTemplateContentsForFileWithUnknownRoot(t *testing.T) { t.Parallel() td := t.TempDir() - logger := newTestLogger(t) ls := NewLanguageServer( @@ -202,12 +176,9 @@ func TestTemplateContentsForFileWithUnknownRoot(t *testing.T) { ls.workspaceRootURI = uri.FromPath(clients.IdentifierGeneric, td) - err := os.MkdirAll(filepath.Join(td, "foo"), 0o755) - if err != nil { - t.Fatalf("failed to create directory %s: %s", filepath.Join(td, "foo"), err) - } + testutil.MustMkdirAll(t, td, "foo") - fileURI := uri.FromPath(clients.IdentifierGeneric, filepath.Join(td, "foo/bar.rego")) + fileURI := uri.FromPath(clients.IdentifierGeneric, filepath.Join(td, "foo", "bar.rego")) ls.cache.SetFileContents(fileURI, "") @@ -216,9 +187,8 @@ func TestTemplateContentsForFileWithUnknownRoot(t *testing.T) { t.Fatalf("unexpected error: %s", err) } - exp := `package foo + exp := "package foo\n\n" -` if exp != newContents { t.Errorf("unexpected content: %s, want %s", newContents, exp) } @@ -227,9 +197,6 @@ func TestTemplateContentsForFileWithUnknownRoot(t *testing.T) { func TestNewFileTemplating(t *testing.T) { t.Parallel() - // set up the workspace content with some example rego and regal config - tempDir := t.TempDir() - files := map[string]string{ ".regal/config.yaml": `rules: idiomatic: @@ -239,6 +206,8 @@ func TestNewFileTemplating(t *testing.T) { `, } + tempDir := testutil.TempDirectoryOf(t, files) + // set up the server and client connections ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -255,10 +224,7 @@ func TestNewFileTemplating(t *testing.T) { return struct{}{}, nil } - ls, connClient, err := createAndInitServer(ctx, newTestLogger(t), tempDir, files, clientHandler) - if err != nil { - t.Fatalf("failed to create and init language server: %s", err) - } + ls, connClient := createAndInitServer(t, ctx, newTestLogger(t), tempDir, clientHandler) go ls.StartTemplateWorker(ctx) @@ -278,17 +244,14 @@ func TestNewFileTemplating(t *testing.T) { } // Touch the new file on disk - newFilePath := filepath.Join(tempDir, "foo/bar/policy_test.rego") + newFilePath := filepath.Join(tempDir, "foo", "bar", "policy_test.rego") newFileURI := uri.FromPath(clients.IdentifierGeneric, newFilePath) - expectedNewFileURI := uri.FromPath(clients.IdentifierGeneric, filepath.Join(tempDir, "foo/bar_test/policy_test.rego")) + expectedNewFileURI := uri.FromPath(clients.IdentifierGeneric, filepath.Join( + tempDir, "foo", "bar_test", "policy_test.rego", + )) - if err := os.MkdirAll(filepath.Dir(newFilePath), 0o755); err != nil { - t.Fatalf("failed to create directory %s: %s", filepath.Dir(newFilePath), err) - } - - if err := os.WriteFile(newFilePath, []byte(""), 0o600); err != nil { - t.Fatalf("failed to write file %s: %s", newFilePath, err) - } + testutil.MustMkdirAll(t, filepath.Dir(newFilePath)) + testutil.MustWriteFile(t, newFilePath, []byte("")) // Client sends workspace/didCreateFiles notification if err := connClient.Notify(ctx, "workspace/didCreateFiles", types.WorkspaceDidCreateFilesParams{ diff --git a/internal/lsp/server_test.go b/internal/lsp/server_test.go index 210a1888..cabb81cc 100644 --- a/internal/lsp/server_test.go +++ b/internal/lsp/server_test.go @@ -6,7 +6,6 @@ import ( "fmt" "io" "net" - "os" "path/filepath" "slices" "sort" @@ -87,29 +86,16 @@ func (tl *testLogger) Write(p []byte) (n int, err error) { } func createAndInitServer( + t *testing.T, ctx context.Context, logger io.Writer, tempDir string, - files map[string]string, clientHandler func(_ context.Context, _ *jsonrpc2.Conn, req *jsonrpc2.Request) (result any, err error), ) ( *LanguageServer, *jsonrpc2.Conn, - error, ) { - var err error - - for f, fc := range files { - err = os.MkdirAll(filepath.Dir(filepath.Join(tempDir, f)), 0o755) - if err != nil { - return nil, nil, fmt.Errorf("failed to create directory: %w", err) - } - - err = os.WriteFile(filepath.Join(tempDir, f), []byte(fc), 0o600) - if err != nil { - return nil, nil, fmt.Errorf("failed to write file: %w", err) - } - } + t.Helper() // This is set due to eventing being so slow in go test -race that we // get flakes. TODO, work out how to avoid needing this in lsp tests. @@ -159,19 +145,17 @@ func createAndInitServer( var response types.InitializeResult - err = connClient.Call(ctx, "initialize", request, &response) - if err != nil { - return nil, nil, fmt.Errorf("failed to initialize %w", err) + if err := connClient.Call(ctx, "initialize", request, &response); err != nil { + t.Fatalf("failed to initialize: %s", err) } // 2. Client sends initialized notification // no response to the call is expected - err = connClient.Call(ctx, "initialized", struct{}{}, nil) - if err != nil { - return nil, nil, fmt.Errorf("failed to complete initialized %w", err) + if err := connClient.Call(ctx, "initialized", struct{}{}, nil); err != nil { + t.Fatalf("failed to complete initialized: %v", err) } - return ls, connClient, nil + return ls, connClient } func createClientHandler( diff --git a/internal/testutil/testutil.go b/internal/testutil/testutil.go index 6cc49414..85b441a9 100644 --- a/internal/testutil/testutil.go +++ b/internal/testutil/testutil.go @@ -1,6 +1,10 @@ package testutil -import "testing" +import ( + "os" + "path/filepath" + "testing" +) func Must[T any](x T, err error) func(t *testing.T) T { return func(t *testing.T) T { @@ -13,3 +17,53 @@ func Must[T any](x T, err error) func(t *testing.T) T { return x } } + +func TempDirectoryOf(t *testing.T, files map[string]string) string { + t.Helper() + + tmpDir := t.TempDir() + + for file, contents := range files { + path := filepath.Join(tmpDir, file) + + MustMkdirAll(t, filepath.Dir(path)) + MustWriteFile(t, path, []byte(contents)) + } + + return tmpDir +} + +func MustMkdirAll(t *testing.T, path ...string) { + t.Helper() + + if err := os.MkdirAll(filepath.Join(path...), 0o755); err != nil { + t.Fatalf("failed to create directory %s: %v", path, err) + } +} + +func MustReadFile(t *testing.T, path string) []byte { + t.Helper() + + contents, err := os.ReadFile(path) + if err != nil { + t.Fatalf("failed to read file %s: %v", path, err) + } + + return contents +} + +func MustWriteFile(t *testing.T, path string, contents []byte) { + t.Helper() + + if err := os.WriteFile(path, contents, 0o600); err != nil { + t.Fatalf("failed to write file %s: %v", path, err) + } +} + +func MustRemove(t *testing.T, path string) { + t.Helper() + + if err := os.Remove(path); err != nil { + t.Fatalf("failed to remove file %s: %v", path, err) + } +} diff --git a/internal/util/util_test.go b/internal/util/util_test.go index 9d0d3f16..a882c480 100644 --- a/internal/util/util_test.go +++ b/internal/util/util_test.go @@ -1,11 +1,12 @@ package util import ( - "os" "path/filepath" "slices" "strings" "testing" + + "github.com/styrainc/regal/internal/testutil" ) func TestFindClosestMatchingRoot(t *testing.T) { @@ -112,22 +113,8 @@ func TestDirCleanUpPaths(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() - tempDir := t.TempDir() - - for k, v := range test.State { - if err := os.MkdirAll(filepath.Dir(filepath.Join(tempDir, k)), 0o755); err != nil { - t.Fatalf("unexpected error: %v", err) - } - - if err := os.WriteFile(filepath.Join(tempDir, k), []byte(v), 0o600); err != nil { - t.Fatalf("unexpected error: %v", err) - } - } - - expected := make([]string, len(test.Expected)) - for i, v := range test.Expected { - expected[i] = filepath.Join(tempDir, v) - } + tempDir := testutil.TempDirectoryOf(t, test.State) + expected := Map(FilepathJoiner(tempDir), test.Expected) additionalPreserveTargets := []string{tempDir} for i, v := range test.AdditionalPreserveTargets { diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index 6c5423f9..442e920c 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -30,7 +30,7 @@ func TestFindRegalDirectory(t *testing.T) { t.Fatal(err) } - path := filepath.Join(root, "/foo/bar/baz") + path := filepath.Join(root, "foo", "bar", "baz") if _, err := FindRegalDirectory(path); err != nil { t.Error(err) @@ -43,7 +43,7 @@ func TestFindRegalDirectory(t *testing.T) { } test.WithTempFS(fs, func(root string) { - path := filepath.Join(root, "/foo/bar/baz") + path := filepath.Join(root, "foo", "bar", "baz") _, err := FindRegalDirectory(path) if err == nil { @@ -119,7 +119,7 @@ func TestFindConfig(t *testing.T) { t.Parallel() test.WithTempFS(testData.FS, func(root string) { - path := filepath.Join(root, "/foo/bar/baz") + path := filepath.Join(root, "foo", "bar", "baz") configFile, err := FindConfig(path) if testData.Error != "" { diff --git a/pkg/fixer/fileprovider/inmem_test.go b/pkg/fixer/fileprovider/inmem_test.go index cc0bebf0..f65062e5 100644 --- a/pkg/fixer/fileprovider/inmem_test.go +++ b/pkg/fixer/fileprovider/inmem_test.go @@ -1,41 +1,25 @@ package fileprovider import ( - "os" "path/filepath" "testing" - "github.com/styrainc/regal/internal/util" + "github.com/styrainc/regal/internal/testutil" ) func TestFromFS(t *testing.T) { t.Parallel() - tempDir := t.TempDir() + tempDir := testutil.TempDirectoryOf(t, map[string]string{ + "foo/bar/baz": "bar", + "bar/foo": "baz", + }) + fp := testutil.Must(NewInMemoryFileProviderFromFS([]string{ + filepath.Join(tempDir, "foo", "bar", "baz"), + filepath.Join(tempDir, "bar", "foo"), + }...))(t) - files := map[string]string{ - tempDir + "/foo/bar/baz": "bar", - tempDir + "/bar/foo": "baz", - } - - for file, content := range files { - /// make the dir - if err := os.MkdirAll(filepath.Dir(file), 0o755); err != nil { - t.Fatal(err) - } - - // write the file - if err := os.WriteFile(file, []byte(content), 0o600); err != nil { - t.Fatal(err) - } - } - - fp, err := NewInMemoryFileProviderFromFS(util.Keys(files)...) - if err != nil { - t.Fatal(err) - } - - if fc, err := fp.Get(tempDir + "/foo/bar/baz"); err != nil || fc != "bar" { + if fc, err := fp.Get(filepath.Join(tempDir, "foo", "bar", "baz")); err != nil || fc != "bar" { t.Fatalf("expected %s, got %s", "bar", fc) } } @@ -43,17 +27,12 @@ func TestFromFS(t *testing.T) { func TestRenameConflict(t *testing.T) { t.Parallel() - fileA := "/foo/bar/baz" - fileB := "/bar/foo" - - files := map[string]string{ - fileA: "bar", - fileB: "baz", - } - - fp := NewInMemoryFileProvider(files) + fp := NewInMemoryFileProvider(map[string]string{ + "/foo/bar/baz": "bar", + "/bar/foo": "baz", + }) - err := fp.Rename(fileA, fileB) + err := fp.Rename("/foo/bar/baz", "/bar/foo") if err == nil { t.Fatal("expected error") }