Loading commands.go +45 −7 Changes for commands.go: 45 added lines, 7 removed lines. Original line number Diff line number Diff line package main import ( "bytes" "encoding/json" "errors" "fmt" Loading Loading @@ -304,19 +305,25 @@ func runFeed(feedPath, directory string, verbose bool, yes bool) error { } } createDB, err := newBaseXCommand(abs, "org.basex.BaseX", "if(not(db:exists('max'))) then db:create('max') else ()") if stat.Mode().IsRegular() { dbPath := filepath.Base(feedAbs) exists, err := baseXResourceExists(abs, dbPath, verbose) if err != nil { return err } if verbose { fmt.Println(formatCommand(createDB)) if exists && !yes { ok, err := confirm(T("question.feed_replace_file"), false) if err != nil { return err } if !ok { return nil } if err := createDB.Run(); err != nil { } if err := ensureBaseXDatabase(abs, verbose); err != nil { return err } if stat.Mode().IsRegular() { query := fmt.Sprintf("db:put('max', '%s', '%s')", feedAbs, filepath.Base(feedAbs)) query := fmt.Sprintf("db:put('max', '%s', '%s')", feedAbs, dbPath) cmd, err := newBaseXCommand(abs, "org.basex.BaseX", query) if err != nil { return err Loading @@ -328,6 +335,9 @@ func runFeed(feedPath, directory string, verbose bool, yes bool) error { return cmd.Run() } if err := ensureBaseXDatabase(abs, verbose); err != nil { return err } feedDir := ensureTrailingPathSeparator(feedAbs) query := fmt.Sprintf("db:put('max', '%s', '')", feedDir) cmd, err := newBaseXCommand(abs, "org.basex.BaseX", query) Loading @@ -341,6 +351,34 @@ func runFeed(feedPath, directory string, verbose bool, yes bool) error { return cmd.Run() } func ensureBaseXDatabase(directory string, verbose bool) error { createDB, err := newBaseXCommand(directory, "org.basex.BaseX", "if(not(db:exists('max'))) then db:create('max') else ()") if err != nil { return err } if verbose { fmt.Println(formatCommand(createDB)) } return createDB.Run() } func baseXResourceExists(directory, dbPath string, verbose bool) (bool, error) { query := fmt.Sprintf("if(db:exists('max')) then db:exists('max', '%s') else false()", dbPath) cmd, err := newBaseXCommand(directory, "org.basex.BaseX", query) if err != nil { return false, err } var out bytes.Buffer cmd.Stdout = &out if verbose { fmt.Println(formatCommand(cmd)) } if err := cmd.Run(); err != nil { return false, err } return strings.EqualFold(strings.TrimSpace(out.String()), "true"), nil } // BaseX treats a trailing separator as "load directory contents"; without it, // a directory feed can be stored under an unexpected path name. func ensureTrailingPathSeparator(path string) string { Loading i18n.go +2 −0 Changes for i18n.go: 2 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -103,6 +103,7 @@ commandes new, projects et cache-clear qui peuvent être lancées aussi en dehor "label.choice": "Choix", "question.install_demo": "Voulez-vous installer une édition de démonstration ?", "question.feed_replace_database": "Attention ! Vous vous apprêtez à écraser la base de données existante, souhaitez-vous continuer ?", "question.feed_replace_file": "Ce fichier existe déjà dans la base BaseX et sera remplacé, souhaitez-vous continuer ?", "err.no_release_available": "aucune release disponible", "err.not_max_directory": "Le dossier n'est pas une installation de MaX.", Loading Loading @@ -218,6 +219,7 @@ which can also be run outside a MaX directory.`, "label.choice": "Choice", "question.install_demo": "Do you want to install a demo edition?", "question.feed_replace_database": "Feeding a directory will replace the entire database. Continue?", "question.feed_replace_file": "This file already exists in the BaseX database and will be replaced. Continue?", "err.no_release_available": "no release available", "err.not_max_directory": "Directory is not a MaX installation.", Loading main.go +1 −1 Changes for main.go: 1 added line, 1 removed line. Original line number Diff line number Diff line Loading @@ -10,7 +10,7 @@ import ( //go:embed max_releases.json var fallbackReleases []byte const version = "0.4.10" const version = "0.4.11" const maxConfigFile = "config.xml" const minSupportedMaxVersion = "0.0.5" Loading main_test.go +84 −10 Changes for main_test.go: 84 added lines, 10 removed lines. Original line number Diff line number Diff line Loading @@ -840,25 +840,29 @@ func TestFeedCommandRunsForFileAndDirectory(t *testing.T) { if err != nil { t.Fatal(err) } if len(lines) != 4 { t.Fatalf("expected 4 java invocations (2 per feed run), got %d:\n%v", len(lines), lines) if len(lines) != 5 { t.Fatalf("expected 5 java invocations (3 for file, 2 for directory), got %d:\n%v", len(lines), lines) } existsQuery := fmt.Sprintf("if(db:exists('max')) then db:exists('max', '%s') else false()", filepath.Base(feedFile)) createDBQuery := "if(not(db:exists('max'))) then db:create('max') else ()" if !strings.Contains(lines[0], createDBQuery) || !strings.Contains(lines[2], createDBQuery) { t.Fatalf("expected create-db query in first and third invocation, got:\n%v", lines) if !strings.Contains(lines[0], existsQuery) { t.Fatalf("expected single-file existence query in first invocation, got:\n%s", lines[0]) } if !strings.Contains(lines[1], createDBQuery) || !strings.Contains(lines[3], createDBQuery) { t.Fatalf("expected create-db query in second and fourth invocation, got:\n%v", lines) } if !strings.Contains(lines[1], fmt.Sprintf("db:put('max', '%s', '%s')", feedFile, filepath.Base(feedFile))) { t.Fatalf("expected single-file db:put query in second invocation, got:\n%s", lines[1]) if !strings.Contains(lines[2], fmt.Sprintf("db:put('max', '%s', '%s')", feedFile, filepath.Base(feedFile))) { t.Fatalf("expected single-file db:put query in third invocation, got:\n%s", lines[2]) } feedDirWithSep := ensureTrailingPathSeparator(feedDir) if !strings.Contains(lines[3], fmt.Sprintf("db:put('max', '%s', '')", feedDirWithSep)) { t.Fatalf("expected directory db:put query in fourth invocation, got:\n%s", lines[3]) if !strings.Contains(lines[4], fmt.Sprintf("db:put('max', '%s', '')", feedDirWithSep)) { t.Fatalf("expected directory db:put query in fifth invocation, got:\n%s", lines[4]) } if strings.Contains(lines[3], "a.xml") || strings.Contains(lines[3], "b.xml") { t.Fatalf("expected no per-file db:put during directory feed, got:\n%s", lines[3]) if strings.Contains(lines[4], "a.xml") || strings.Contains(lines[4], "b.xml") { t.Fatalf("expected no per-file db:put during directory feed, got:\n%s", lines[4]) } } Loading Loading @@ -934,6 +938,70 @@ func TestFeedDirectoryConfirmationCanAbort(t *testing.T) { } } func TestFeedExistingFileConfirmationCanAbort(t *testing.T) { previousLocale := currentLocale setLocale(localeEN) t.Cleanup(func() { setLocale(previousLocale) }) tmpHome := t.TempDir() prevHome := userMaxDir prevCache := cacheDir userMaxDir = tmpHome cacheDir = filepath.Join(userMaxDir, "cache") t.Cleanup(func() { userMaxDir = prevHome cacheDir = prevCache }) projectDir := filepath.Join(t.TempDir(), "project") if err := os.MkdirAll(projectDir, 0o755); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(projectDir, maxConfigFile), []byte(configInitTemplate), 0o644); err != nil { t.Fatal(err) } feedFile := filepath.Join(projectDir, "single.xml") if err := os.WriteFile(feedFile, []byte("<root/>"), 0o644); err != nil { t.Fatal(err) } logPath := filepath.Join(t.TempDir(), "java.log") t.Setenv("CLIMAX_TEST_JAVA_LOG", logPath) t.Setenv("CLIMAX_TEST_BASEX_EXISTING_PATH", filepath.Base(feedFile)) if err := installFakeBundledJava(tmpHome); err != nil { t.Fatal(err) } withStdinContent(t, "n\n", func() { out := captureStdoutForMainTest(t, func() { root := newRootCmd() root.SetArgs([]string{"feed", feedFile, "--directory", projectDir}) if err := root.Execute(); err != nil { t.Fatalf("feed command failed after declined confirmation: %v", err) } }) if !strings.Contains(out, "already exists in the BaseX database") { t.Fatalf("expected existing-file warning in confirmation prompt, got:\n%s", out) } }) lines, err := readLogLines(logPath) if err != nil { t.Fatal(err) } if len(lines) != 1 { t.Fatalf("expected only the existence check after declined confirmation, got %d:\n%v", len(lines), lines) } if !strings.Contains(lines[0], "db:exists('max', 'single.xml')") { t.Fatalf("expected single-file existence query, got:\n%s", lines[0]) } if strings.Contains(lines[0], "db:put") || strings.Contains(lines[0], "db:create") { t.Fatalf("expected no database modification after declined confirmation, got:\n%s", lines[0]) } } func TestBasexGUICommandRunsGUIClass(t *testing.T) { if runtime.GOOS == "windows" { t.Skip("test uses a shell script as fake bundled java") Loading Loading @@ -1061,6 +1129,12 @@ func installFakeBundledJava(home string) error { "if [ -n \"$CLIMAX_TEST_JAVA_LOG\" ]; then\n" + " printf '%s\\n' \"$*\" >> \"$CLIMAX_TEST_JAVA_LOG\"\n" + "fi\n" + "if [ -n \"$CLIMAX_TEST_BASEX_EXISTING_PATH\" ]; then\n" + " case \"$*\" in\n" + " *\"db:exists('max', '$CLIMAX_TEST_BASEX_EXISTING_PATH')\"*) printf 'true\\n' ;;\n" + " *\"db:exists('max', \"*) printf 'false\\n' ;;\n" + " esac\n" + "fi\n" + "if [ -n \"$CLIMAX_TEST_JAVA_PWD_LOG\" ]; then\n" + " pwd >> \"$CLIMAX_TEST_JAVA_PWD_LOG\"\n" + "fi\n" Loading Loading
commands.go +45 −7 Changes for commands.go: 45 added lines, 7 removed lines. Original line number Diff line number Diff line package main import ( "bytes" "encoding/json" "errors" "fmt" Loading Loading @@ -304,19 +305,25 @@ func runFeed(feedPath, directory string, verbose bool, yes bool) error { } } createDB, err := newBaseXCommand(abs, "org.basex.BaseX", "if(not(db:exists('max'))) then db:create('max') else ()") if stat.Mode().IsRegular() { dbPath := filepath.Base(feedAbs) exists, err := baseXResourceExists(abs, dbPath, verbose) if err != nil { return err } if verbose { fmt.Println(formatCommand(createDB)) if exists && !yes { ok, err := confirm(T("question.feed_replace_file"), false) if err != nil { return err } if !ok { return nil } if err := createDB.Run(); err != nil { } if err := ensureBaseXDatabase(abs, verbose); err != nil { return err } if stat.Mode().IsRegular() { query := fmt.Sprintf("db:put('max', '%s', '%s')", feedAbs, filepath.Base(feedAbs)) query := fmt.Sprintf("db:put('max', '%s', '%s')", feedAbs, dbPath) cmd, err := newBaseXCommand(abs, "org.basex.BaseX", query) if err != nil { return err Loading @@ -328,6 +335,9 @@ func runFeed(feedPath, directory string, verbose bool, yes bool) error { return cmd.Run() } if err := ensureBaseXDatabase(abs, verbose); err != nil { return err } feedDir := ensureTrailingPathSeparator(feedAbs) query := fmt.Sprintf("db:put('max', '%s', '')", feedDir) cmd, err := newBaseXCommand(abs, "org.basex.BaseX", query) Loading @@ -341,6 +351,34 @@ func runFeed(feedPath, directory string, verbose bool, yes bool) error { return cmd.Run() } func ensureBaseXDatabase(directory string, verbose bool) error { createDB, err := newBaseXCommand(directory, "org.basex.BaseX", "if(not(db:exists('max'))) then db:create('max') else ()") if err != nil { return err } if verbose { fmt.Println(formatCommand(createDB)) } return createDB.Run() } func baseXResourceExists(directory, dbPath string, verbose bool) (bool, error) { query := fmt.Sprintf("if(db:exists('max')) then db:exists('max', '%s') else false()", dbPath) cmd, err := newBaseXCommand(directory, "org.basex.BaseX", query) if err != nil { return false, err } var out bytes.Buffer cmd.Stdout = &out if verbose { fmt.Println(formatCommand(cmd)) } if err := cmd.Run(); err != nil { return false, err } return strings.EqualFold(strings.TrimSpace(out.String()), "true"), nil } // BaseX treats a trailing separator as "load directory contents"; without it, // a directory feed can be stored under an unexpected path name. func ensureTrailingPathSeparator(path string) string { Loading
i18n.go +2 −0 Changes for i18n.go: 2 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -103,6 +103,7 @@ commandes new, projects et cache-clear qui peuvent être lancées aussi en dehor "label.choice": "Choix", "question.install_demo": "Voulez-vous installer une édition de démonstration ?", "question.feed_replace_database": "Attention ! Vous vous apprêtez à écraser la base de données existante, souhaitez-vous continuer ?", "question.feed_replace_file": "Ce fichier existe déjà dans la base BaseX et sera remplacé, souhaitez-vous continuer ?", "err.no_release_available": "aucune release disponible", "err.not_max_directory": "Le dossier n'est pas une installation de MaX.", Loading Loading @@ -218,6 +219,7 @@ which can also be run outside a MaX directory.`, "label.choice": "Choice", "question.install_demo": "Do you want to install a demo edition?", "question.feed_replace_database": "Feeding a directory will replace the entire database. Continue?", "question.feed_replace_file": "This file already exists in the BaseX database and will be replaced. Continue?", "err.no_release_available": "no release available", "err.not_max_directory": "Directory is not a MaX installation.", Loading
main.go +1 −1 Changes for main.go: 1 added line, 1 removed line. Original line number Diff line number Diff line Loading @@ -10,7 +10,7 @@ import ( //go:embed max_releases.json var fallbackReleases []byte const version = "0.4.10" const version = "0.4.11" const maxConfigFile = "config.xml" const minSupportedMaxVersion = "0.0.5" Loading
main_test.go +84 −10 Changes for main_test.go: 84 added lines, 10 removed lines. Original line number Diff line number Diff line Loading @@ -840,25 +840,29 @@ func TestFeedCommandRunsForFileAndDirectory(t *testing.T) { if err != nil { t.Fatal(err) } if len(lines) != 4 { t.Fatalf("expected 4 java invocations (2 per feed run), got %d:\n%v", len(lines), lines) if len(lines) != 5 { t.Fatalf("expected 5 java invocations (3 for file, 2 for directory), got %d:\n%v", len(lines), lines) } existsQuery := fmt.Sprintf("if(db:exists('max')) then db:exists('max', '%s') else false()", filepath.Base(feedFile)) createDBQuery := "if(not(db:exists('max'))) then db:create('max') else ()" if !strings.Contains(lines[0], createDBQuery) || !strings.Contains(lines[2], createDBQuery) { t.Fatalf("expected create-db query in first and third invocation, got:\n%v", lines) if !strings.Contains(lines[0], existsQuery) { t.Fatalf("expected single-file existence query in first invocation, got:\n%s", lines[0]) } if !strings.Contains(lines[1], createDBQuery) || !strings.Contains(lines[3], createDBQuery) { t.Fatalf("expected create-db query in second and fourth invocation, got:\n%v", lines) } if !strings.Contains(lines[1], fmt.Sprintf("db:put('max', '%s', '%s')", feedFile, filepath.Base(feedFile))) { t.Fatalf("expected single-file db:put query in second invocation, got:\n%s", lines[1]) if !strings.Contains(lines[2], fmt.Sprintf("db:put('max', '%s', '%s')", feedFile, filepath.Base(feedFile))) { t.Fatalf("expected single-file db:put query in third invocation, got:\n%s", lines[2]) } feedDirWithSep := ensureTrailingPathSeparator(feedDir) if !strings.Contains(lines[3], fmt.Sprintf("db:put('max', '%s', '')", feedDirWithSep)) { t.Fatalf("expected directory db:put query in fourth invocation, got:\n%s", lines[3]) if !strings.Contains(lines[4], fmt.Sprintf("db:put('max', '%s', '')", feedDirWithSep)) { t.Fatalf("expected directory db:put query in fifth invocation, got:\n%s", lines[4]) } if strings.Contains(lines[3], "a.xml") || strings.Contains(lines[3], "b.xml") { t.Fatalf("expected no per-file db:put during directory feed, got:\n%s", lines[3]) if strings.Contains(lines[4], "a.xml") || strings.Contains(lines[4], "b.xml") { t.Fatalf("expected no per-file db:put during directory feed, got:\n%s", lines[4]) } } Loading Loading @@ -934,6 +938,70 @@ func TestFeedDirectoryConfirmationCanAbort(t *testing.T) { } } func TestFeedExistingFileConfirmationCanAbort(t *testing.T) { previousLocale := currentLocale setLocale(localeEN) t.Cleanup(func() { setLocale(previousLocale) }) tmpHome := t.TempDir() prevHome := userMaxDir prevCache := cacheDir userMaxDir = tmpHome cacheDir = filepath.Join(userMaxDir, "cache") t.Cleanup(func() { userMaxDir = prevHome cacheDir = prevCache }) projectDir := filepath.Join(t.TempDir(), "project") if err := os.MkdirAll(projectDir, 0o755); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(projectDir, maxConfigFile), []byte(configInitTemplate), 0o644); err != nil { t.Fatal(err) } feedFile := filepath.Join(projectDir, "single.xml") if err := os.WriteFile(feedFile, []byte("<root/>"), 0o644); err != nil { t.Fatal(err) } logPath := filepath.Join(t.TempDir(), "java.log") t.Setenv("CLIMAX_TEST_JAVA_LOG", logPath) t.Setenv("CLIMAX_TEST_BASEX_EXISTING_PATH", filepath.Base(feedFile)) if err := installFakeBundledJava(tmpHome); err != nil { t.Fatal(err) } withStdinContent(t, "n\n", func() { out := captureStdoutForMainTest(t, func() { root := newRootCmd() root.SetArgs([]string{"feed", feedFile, "--directory", projectDir}) if err := root.Execute(); err != nil { t.Fatalf("feed command failed after declined confirmation: %v", err) } }) if !strings.Contains(out, "already exists in the BaseX database") { t.Fatalf("expected existing-file warning in confirmation prompt, got:\n%s", out) } }) lines, err := readLogLines(logPath) if err != nil { t.Fatal(err) } if len(lines) != 1 { t.Fatalf("expected only the existence check after declined confirmation, got %d:\n%v", len(lines), lines) } if !strings.Contains(lines[0], "db:exists('max', 'single.xml')") { t.Fatalf("expected single-file existence query, got:\n%s", lines[0]) } if strings.Contains(lines[0], "db:put") || strings.Contains(lines[0], "db:create") { t.Fatalf("expected no database modification after declined confirmation, got:\n%s", lines[0]) } } func TestBasexGUICommandRunsGUIClass(t *testing.T) { if runtime.GOOS == "windows" { t.Skip("test uses a shell script as fake bundled java") Loading Loading @@ -1061,6 +1129,12 @@ func installFakeBundledJava(home string) error { "if [ -n \"$CLIMAX_TEST_JAVA_LOG\" ]; then\n" + " printf '%s\\n' \"$*\" >> \"$CLIMAX_TEST_JAVA_LOG\"\n" + "fi\n" + "if [ -n \"$CLIMAX_TEST_BASEX_EXISTING_PATH\" ]; then\n" + " case \"$*\" in\n" + " *\"db:exists('max', '$CLIMAX_TEST_BASEX_EXISTING_PATH')\"*) printf 'true\\n' ;;\n" + " *\"db:exists('max', \"*) printf 'false\\n' ;;\n" + " esac\n" + "fi\n" + "if [ -n \"$CLIMAX_TEST_JAVA_PWD_LOG\" ]; then\n" + " pwd >> \"$CLIMAX_TEST_JAVA_PWD_LOG\"\n" + "fi\n" Loading