diff --git a/.testcoverage.yaml b/.testcoverage.yaml index cbbc600..197c2ea 100644 --- a/.testcoverage.yaml +++ b/.testcoverage.yaml @@ -33,3 +33,8 @@ exclude: - ^internal/health # simply just configuration for health check - ^internal/pluginloader/process_handler\.go # too much effort to test - ^cmd/limepipes-cli/cmd/arguments.go$ # only setup code + - ^cmd/limepipes-cli/cmd/import.go$ # only setup code + - ^cmd/limepipes-cli/cmd/parse.go$ # only setup code + - ^cmd/limepipes-cli/cmd/root.go$ # only setup code + - ^cmd/limepipes-cli/cmd/main.go$ # only setup code + - ^cmd/limepipes-cli/cmd/setup.go$ # only setup code diff --git a/cmd/limepipes-cli/cmd/common.go b/cmd/limepipes-cli/cmd/common.go index bef1bbf..5db730a 100644 --- a/cmd/limepipes-cli/cmd/common.go +++ b/cmd/limepipes-cli/cmd/common.go @@ -4,15 +4,10 @@ import ( "fmt" "github.com/rs/zerolog/log" "github.com/spf13/afero" - "github.com/tomvodi/limepipes-plugin-api/plugin/v1/fileformat" "github.com/tomvodi/limepipes-plugin-api/plugin/v1/messages" "github.com/tomvodi/limepipes/cmd/limepipes-cli/importtype" - "github.com/tomvodi/limepipes/internal/api" "github.com/tomvodi/limepipes/internal/common" - "github.com/tomvodi/limepipes/internal/config" - "github.com/tomvodi/limepipes/internal/database" "github.com/tomvodi/limepipes/internal/interfaces" - "github.com/tomvodi/limepipes/internal/pluginloader" "io/fs" "os" "path/filepath" @@ -27,41 +22,6 @@ type GetFilesOptions struct { FileExtensions []string // only search for files with these extensions } -func setupPluginLoader( - afs afero.Fs, - cfg *config.Config, -) (*pluginloader.Loader, error) { - // TODO: Load plugins from config - LoadPlugins := []string{ - fileformat.Format_BWW.String(), - } - - var pluginProcHandler interfaces.PluginProcessHandler = pluginloader.NewProcessHandler(LoadPlugins) - pluginLoader := pluginloader.NewPluginLoader( - afs, - pluginProcHandler, - LoadPlugins, - ) - err := pluginLoader.LoadPluginsFromDir(cfg.PluginsDirectoryPath) - if err != nil { - return nil, fmt.Errorf("failed loading plugins: %w", err) - } - - return pluginLoader, nil -} - -func setupDbService( - cfg config.DbConfig, -) (*database.Service, error) { - db, err := database.GetInitPostgreSQLDB(cfg) - if err != nil { - return nil, fmt.Errorf("failed initializing database: %s", err.Error()) - } - ginValidator := api.NewGinValidator() - apiModelValidator := api.NewAPIModelValidator(ginValidator) - return database.NewDbDataService(db, apiModelValidator), nil -} - // invalidImportTypesPassed takes a list of passed import types and returns // a list of the import types that are not valid. // It returns nil if all given import types are valid. @@ -206,6 +166,10 @@ func fileHasValidExtension( // checkForInvalidImportTypes checks if the given import types are valid. func checkForInvalidImportTypes(iTypes []string) error { + if len(iTypes) == 0 { + return fmt.Errorf("no import types passed") + } + invalidTypes := invalidImportTypesPassed(iTypes) if len(invalidTypes) > 0 { return fmt.Errorf( @@ -216,26 +180,43 @@ func checkForInvalidImportTypes(iTypes []string) error { return nil } +// moveProcessFile moves the file to the output directory if the MoveToOutputDir option is set. func moveProcessFile( pfc *ProcessFileContext, opts *Options, pfo *ProcessFilesOptions, ) error { if pfo.MoveToOutputDir { - err := moveFileToDir(pfc.Filepath, opts.OutputDir) + err := moveFileToDir(pfo.Fs, pfc.Filepath, opts.OutputDir) if err != nil { log.Error().Err(err).Msgf("failed moving file %s to dir %s", pfc.Filepath, opts.OutputDir) + return err } } return nil } +func moveFileToDir( + afs afero.Fs, + file string, + dir string, +) error { + if dir == "" { + return fmt.Errorf("no output directory given") + } + + fileName := filepath.Base(file) + newPath := filepath.Join(dir, fileName) + return afs.Rename(file, newPath) +} + func parseProcessFile( pfc *ProcessFileContext, opts *Options, + pfo *ProcessFilesOptions, ) ([]*messages.ImportedTune, error) { - tunes, err := parseFile(pfc.Filepath, pfc.PluginLoader) + tunes, err := parseFile(pfc, pfo) if err != nil { if opts.SkipFailedFiles { log.Error().Err(err).Msgf("failed parsing file %s", pfc.Filepath) @@ -249,28 +230,31 @@ func parseProcessFile( } func parseFile( - fPath string, - pluginLoader interfaces.PluginLoader, + pfc *ProcessFileContext, + pfo *ProcessFilesOptions, ) ([]*messages.ImportedTune, error) { - fExt := filepath.Ext(fPath) + fExt := filepath.Ext(pfc.Filepath) if fExt == "" { - return nil, fmt.Errorf("import file %s does not have an extension", fPath) + return nil, fmt.Errorf( + "import file %s does not have an extension", + pfc.Filepath, + ) } - filePlugin, err := pluginLoader.PluginForFileExtension(fExt) + filePlugin, err := pfc.PluginLoader.PluginForFileExtension(fExt) if err != nil { return nil, fmt.Errorf("failed getting plugin for file %s with extension %s", - fPath, fExt) + pfc.Filepath, fExt) } - fileData, err := os.ReadFile(fPath) + fileData, err := afero.ReadFile(pfo.Fs, pfc.Filepath) if err != nil { - return nil, fmt.Errorf("failed reading file %s", fPath) + return nil, fmt.Errorf("failed reading file %s", pfc.Filepath) } tunesImport, err := filePlugin.Import(fileData) if err != nil { - return nil, fmt.Errorf("failed parsing file %s", fPath) + return nil, fmt.Errorf("failed parsing file %s", pfc.Filepath) } return tunesImport.ImportedTunes, nil @@ -322,9 +306,3 @@ func validFileExtensionsForImportTypes( return fileExtensions, nil } - -func moveFileToDir(file string, dir string) error { - fileName := filepath.Base(file) - newPath := filepath.Join(dir, fileName) - return os.Rename(file, newPath) -} diff --git a/cmd/limepipes-cli/cmd/common_test.go b/cmd/limepipes-cli/cmd/common_test.go index 3cc9bff..87b76fd 100644 --- a/cmd/limepipes-cli/cmd/common_test.go +++ b/cmd/limepipes-cli/cmd/common_test.go @@ -68,7 +68,9 @@ func Test_getAllFilesFromPaths(t *testing.T) { { name: "no file extensions given", prepare: func(f *fields) { - f.opts.FileExtensions = nil + f.opts = &GetFilesOptions{ + FileExtensions: nil, + } f.wantErr = true }, }, @@ -160,3 +162,121 @@ func Test_getAllFilesFromPaths(t *testing.T) { }) } } + +func Test_checkForInvalidImportTypes(t *testing.T) { + utils.SetupConsoleLogger() + type args struct { + iTypes []string + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "no import types passed", + args: args{ + iTypes: []string{}, + }, + wantErr: true, + }, + { + name: "all possible import types passed", + args: args{ + iTypes: importtype.AllTypes(), + }, + wantErr: false, + }, + { + name: "an invalid import type passed", + args: args{ + iTypes: []string{"invalid"}, + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := checkForInvalidImportTypes(tt.args.iTypes); (err != nil) != tt.wantErr { + t.Errorf("checkForInvalidImportTypes() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func Test_moveProcessFile(t *testing.T) { + g := NewGomegaWithT(t) + type fields struct { + pfc *ProcessFileContext + opts *Options + pfo *ProcessFilesOptions + expectedFile string + wantErr bool + } + tests := []struct { + name string + prepare func(f *fields) + }{ + { + name: "no move configured", + prepare: func(f *fields) { + f.pfo.MoveToOutputDir = false + f.pfc.Filepath = "tune1.bww" + f.wantErr = false + }, + }, + { + name: "move configured but no output dir given", + prepare: func(f *fields) { + f.pfo.MoveToOutputDir = true + f.pfc.Filepath = "tune1.bww" + f.wantErr = true + }, + }, + { + name: "move and output dir configured", + prepare: func(f *fields) { + f.pfo.MoveToOutputDir = true + f.pfc.Filepath = "tune1.bww" + f.opts.OutputDir = "output" + f.expectedFile = "output/tune1.bww" + f.wantErr = false + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + f := &fields{ + pfc: &ProcessFileContext{}, + opts: &Options{}, + pfo: &ProcessFilesOptions{}, + } + + if tt.prepare != nil { + tt.prepare(f) + } + + afs := afero.NewMemMapFs() + f.pfo.Fs = afs + _, err := afs.Create(f.pfc.Filepath) + g.Expect(err).To(BeNil()) + + err = moveProcessFile( + f.pfc, + f.opts, + f.pfo, + ) + if f.wantErr { + g.Expect(err).Should(HaveOccurred()) + } else { + g.Expect(err).ShouldNot(HaveOccurred()) + } + + if f.expectedFile != "" { + afs := f.pfo.Fs + _, err := afs.Stat(f.expectedFile) + g.Expect(err).NotTo(HaveOccurred()) + } + }) + } +} diff --git a/cmd/limepipes-cli/cmd/process_files.go b/cmd/limepipes-cli/cmd/process_files.go index 5573693..6ef9404 100644 --- a/cmd/limepipes-cli/cmd/process_files.go +++ b/cmd/limepipes-cli/cmd/process_files.go @@ -206,7 +206,7 @@ func processFile( opts *Options, pfo *ProcessFilesOptions, ) error { - tunes, err := parseProcessFile(pfc, opts) + tunes, err := parseProcessFile(pfc, opts, pfo) if errors.Is(err, common.ErrSkipped) { return nil } diff --git a/cmd/limepipes-cli/cmd/setup.go b/cmd/limepipes-cli/cmd/setup.go new file mode 100644 index 0000000..a558aee --- /dev/null +++ b/cmd/limepipes-cli/cmd/setup.go @@ -0,0 +1,47 @@ +package cmd + +import ( + "fmt" + "github.com/spf13/afero" + "github.com/tomvodi/limepipes-plugin-api/plugin/v1/fileformat" + "github.com/tomvodi/limepipes/internal/api" + "github.com/tomvodi/limepipes/internal/config" + "github.com/tomvodi/limepipes/internal/database" + "github.com/tomvodi/limepipes/internal/interfaces" + "github.com/tomvodi/limepipes/internal/pluginloader" +) + +func setupPluginLoader( + afs afero.Fs, + cfg *config.Config, +) (*pluginloader.Loader, error) { + // TODO: Load plugins from config + LoadPlugins := []string{ + fileformat.Format_BWW.String(), + } + + var pluginProcHandler interfaces.PluginProcessHandler = pluginloader.NewProcessHandler(LoadPlugins) + pluginLoader := pluginloader.NewPluginLoader( + afs, + pluginProcHandler, + LoadPlugins, + ) + err := pluginLoader.LoadPluginsFromDir(cfg.PluginsDirectoryPath) + if err != nil { + return nil, fmt.Errorf("failed loading plugins: %w", err) + } + + return pluginLoader, nil +} + +func setupDbService( + cfg config.DbConfig, +) (*database.Service, error) { + db, err := database.GetInitPostgreSQLDB(cfg) + if err != nil { + return nil, fmt.Errorf("failed initializing database: %s", err.Error()) + } + ginValidator := api.NewGinValidator() + apiModelValidator := api.NewAPIModelValidator(ginValidator) + return database.NewDbDataService(db, apiModelValidator), nil +}