Commit 3f971414 authored by Mickaël Desfrênes's avatar Mickaël Desfrênes
Browse files

add a warning before feeding existing file + version bump

parent 55d4538f
Loading
Loading
Loading
Loading
+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"
@@ -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
@@ -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)
@@ -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 {
+2 −0
Changes for i18n.go: 2 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -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.",
@@ -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.",
+1 −1
Changes for main.go: 1 added line, 1 removed line.
Original line number Diff line number Diff line
@@ -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"
+84 −10
Changes for main_test.go: 84 added lines, 10 removed lines.
Original line number Diff line number Diff line
@@ -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])
	}
}

@@ -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")
@@ -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"