;;  -*- lexical-binding: t -*-

(require 'test/common)


(eldev-ert-defargtest eldev-upgrade-nothing-to-do-1 (mode)
                      ('everything 'explicit 'external)
  (eldev--test-with-external-dir "missing-dependency-a" ()
    :enabled (eq mode 'external)
    (let ((eldev--test-project "missing-dependency-a"))
      (eldev--test-delete-cache)
      (eldev--test-run nil ("--setup" `(eldev-use-package-archive `("archive-a" . ,(expand-file-name "../package-archive-a")))
                            "version" "dependency-a")
        (should (string= stdout "dependency-a 1.0\n"))
        (should (= exit-code 0)))
      (eldev--test-run nil (:eval `("--setup" `(eldev-use-package-archive `("archive-a" . ,(expand-file-name "../package-archive-a")))
                                   ,@(when (eq mode 'external) `(,(format "--external=%s" external-dir)))
                                   "upgrade" ,@(when (eq mode 'explicit) `("dependency-a"))))
        (if (eq mode 'external)
            (progn (should (string-match-p "external package directory" stderr))
                   (should (= exit-code 1)))
          (should (string= stdout "All dependencies are up-to-date\n"))
          (should (= exit-code 0)))))))


(ert-deftest eldev-upgrade-other-archive-1 ()
  (let ((eldev--test-project "missing-dependency-a"))
    (eldev--test-delete-cache)
    (eldev--test-run nil ("version" "dependency-a")
      (should (string-match-p "dependency-a" stderr))
      (should (string= stdout ""))
      (should (= exit-code 1)))
    (eldev--test-run nil ("--setup" `(eldev-use-package-archive `("archive-a" . ,(expand-file-name "../package-archive-a")))
                          "version" "dependency-a")
      (should (string= stdout "dependency-a 1.0\n"))
      (should (= exit-code 0)))
    ;; Package archives A and B have different versions of `dependency-a'.
    (eldev--test-run nil ("--setup" `(eldev-use-package-archive `("archive-b" . ,(expand-file-name "../package-archive-b")))
                          "upgrade")
      (should (string= stdout "Upgraded or installed 1 dependency package\n"))
      (should (= exit-code 0)))
    ;; Package archive in setup form shouldn't be needed at this
    ;; point: dependency is already installed.
    (eldev--test-run nil ("version" "dependency-a")
      (should (string= stdout "dependency-a 1.1\n"))
      (should (= exit-code 0)))))

;; Exactly like the previous test, only we keep archive name to make
;; sure that 'upgrade' command knows to refetch its contents.
(ert-deftest eldev-upgrade-same-archive-1 ()
  (let ((eldev--test-project "missing-dependency-a"))
    (eldev--test-delete-cache)
    (eldev--test-run nil ("version" "dependency-a")
      (should (string-match-p "dependency-a" stderr))
      (should (string= stdout ""))
      (should (= exit-code 1)))
    (eldev--test-run nil ("--setup" `(eldev-use-package-archive `("archive-a" . ,(expand-file-name "../package-archive-a")))
                          "version" "dependency-a")
      (should (string= stdout "dependency-a 1.0\n"))
      (should (= exit-code 0)))
    ;; Package archives A and B have different versions of `dependency-a'.
    (eldev--test-run nil ("--setup" `(eldev-use-package-archive `("archive-a" . ,(expand-file-name "../package-archive-b")))
                          "upgrade")
      (should (string= stdout "Upgraded or installed 1 dependency package\n"))
      (should (= exit-code 0)))
    ;; Package archive in setup form shouldn't be needed at this point:
    ;; dependency is already installed.
    (eldev--test-run nil ("version" "dependency-a")
      (should (string= stdout "dependency-a 1.1\n"))
      (should (= exit-code 0)))))


(ert-deftest eldev-upgrade-wrong-dependency-1 ()
  (eldev--test-run "trivial-project" ("upgrade" "doesnt-depend-on-this")
    (should (= exit-code 1))))


(ert-deftest eldev-upgrade-other-archive-2 ()
  (let ((eldev--test-project "missing-dependency-b"))
    (eldev--test-delete-cache)
    (eldev--test-run nil ("version" "dependency-b")
      (should (string-match-p "dependency-b" stderr))
      (should (string= stdout ""))
      (should (= exit-code 1)))
    (eldev--test-run nil ("--setup" `(eldev-use-package-archive `("archive-a" . ,(expand-file-name "../package-archive-a")))
                          "version" "dependency-a" "dependency-b")
      (should (string= stdout "dependency-a 1.0\ndependency-b 1.0\n"))
      (should (= exit-code 0)))
    (eldev--test-run nil ("--setup" `(eldev-use-package-archive `("archive-b" . ,(expand-file-name "../package-archive-b")))
                          "upgrade")
      (should (string= stdout "Upgraded or installed 2 dependency packages\n"))
      (should (= exit-code 0)))
    (eldev--test-run nil ("version" "dependency-a" "dependency-b")
      (should (string= stdout "dependency-a 1.1\ndependency-b 1.1\n"))
      (should (= exit-code 0)))))

;; Like above, but explicitly list what to upgrade.
(ert-deftest eldev-upgrade-other-archive-3 ()
  (let ((eldev--test-project "missing-dependency-b"))
    (eldev--test-delete-cache)
    (eldev--test-run nil ("version" "dependency-b")
      (should (string-match-p "dependency-b" stderr))
      (should (string= stdout ""))
      (should (= exit-code 1)))
    (eldev--test-run nil ("--setup" `(eldev-use-package-archive `("archive-a" . ,(expand-file-name "../package-archive-a")))
                          "version" "dependency-a" "dependency-b")
      (should (string= stdout "dependency-a 1.0\ndependency-b 1.0\n"))
      (should (= exit-code 0)))
    (eldev--test-run nil ("--setup" `(eldev-use-package-archive `("archive-b" . ,(expand-file-name "../package-archive-b")))
                          "upgrade" "dependency-a" "dependency-b")
      (should (string= stdout "Upgraded or installed 2 dependency packages\n"))
      (should (= exit-code 0)))
    (eldev--test-run nil ("version" "dependency-a" "dependency-b")
      (should (string= stdout "dependency-a 1.1\ndependency-b 1.1\n"))
      (should (= exit-code 0)))))

;; Like above, but explicitly upgrade only `dependency-a'.
(ert-deftest eldev-upgrade-other-archive-4 ()
  (let ((eldev--test-project "missing-dependency-b"))
    (eldev--test-delete-cache)
    (eldev--test-run nil ("version" "dependency-b")
      (should (string-match-p "dependency-b" stderr))
      (should (string= stdout ""))
      (should (= exit-code 1)))
    (eldev--test-run nil ("--setup" `(eldev-use-package-archive `("archive-a" . ,(expand-file-name "../package-archive-a")))
                          "version" "dependency-a" "dependency-b")
      (should (string= stdout "dependency-a 1.0\ndependency-b 1.0\n"))
      (should (= exit-code 0)))
    (eldev--test-run nil ("--setup" `(eldev-use-package-archive `("archive-b" . ,(expand-file-name "../package-archive-b")))
                          "upgrade" "dependency-a")
      (should (string= stdout "Upgraded or installed 1 dependency package\n"))
      (should (= exit-code 0)))
    (eldev--test-run nil ("version" "dependency-a" "dependency-b")
      (should (string= stdout "dependency-a 1.1\ndependency-b 1.0\n"))
      (should (= exit-code 0)))))

;; Like above, but explicitly upgrade only `dependency-b'; however
;; `dependency-a' must get upgraded too, since new `dependency-b'
;; version requires it.
(ert-deftest eldev-upgrade-other-archive-5 ()
  (let ((eldev--test-project "missing-dependency-b"))
    (eldev--test-delete-cache)
    (eldev--test-run nil ("version" "dependency-b")
      (should (string-match-p "dependency-b" stderr))
      (should (string= stdout ""))
      (should (= exit-code 1)))
    (eldev--test-run nil ("--setup" `(eldev-use-package-archive `("archive-a" . ,(expand-file-name "../package-archive-a")))
                          "version" "dependency-a" "dependency-b")
      (should (string= stdout "dependency-a 1.0\ndependency-b 1.0\n"))
      (should (= exit-code 0)))
    (eldev--test-run nil ("--setup" `(eldev-use-package-archive `("archive-b" . ,(expand-file-name "../package-archive-b")))
                          "upgrade" "dependency-b")
      (should (string= stdout "Upgraded or installed 2 dependency packages\n"))
      (should (= exit-code 0)))
    (eldev--test-run nil ("version" "dependency-a" "dependency-b")
      (should (string= stdout "dependency-a 1.1\ndependency-b 1.1\n"))
      (should (= exit-code 0)))))


(ert-deftest eldev-upgrade-dry-run-1 ()
  (let ((eldev--test-project "missing-dependency-a"))
    (eldev--test-delete-cache)
    (eldev--test-run nil ("--setup" `(eldev-use-package-archive `("archive-a" . ,(expand-file-name "../package-archive-a")))
                          "version" "dependency-a")
      (should (string= stdout "dependency-a 1.0\n"))
      (should (= exit-code 0)))
    (eldev--test-run nil ("--setup" `(eldev-use-package-archive `("archive-b" . ,(expand-file-name "../package-archive-b")))
                          "upgrade" "--dry-run")
      ;; `--dry-run' intentionally produces exactly the same output.
      (should (string= stdout "Upgraded or installed 1 dependency package\n"))
      (should (= exit-code 0)))
    (eldev--test-run nil ("version" "dependency-a")
      ;; But it doesn't actually upgrade anything.
      (should (string= stdout "dependency-a 1.0\n"))
      (should (= exit-code 0)))))


(ert-deftest eldev-upgrade-priorities-1 ()
  (let ((eldev--test-project "missing-dependency-a"))
    (eldev--test-delete-cache)
    ;; Although version 1.1 is available, it must not be used because
    ;; `archive-b' has a lower priority.
    (eldev--test-run nil ("--setup" `(eldev-use-package-archive `("archive-a" . ,(expand-file-name "../package-archive-a")) 100)
                          "--setup" `(eldev-use-package-archive `("archive-b" . ,(expand-file-name "../package-archive-b")) 0)
                          "version" "dependency-a")
      (should (string= stdout "dependency-a 1.0\n"))
      (should (= exit-code 0)))
    ;; Upgrading also should do nothing.
    (eldev--test-run nil ("--setup" `(eldev-use-package-archive `("archive-a" . ,(expand-file-name "../package-archive-a")) 100)
                          "--setup" `(eldev-use-package-archive `("archive-b" . ,(expand-file-name "../package-archive-b")) 0)
                          "upgrade" "dependency-a")
      (should (string= stdout "All dependencies are up-to-date\n"))
      (should (= exit-code 0)))
    (eldev--test-run nil ("version" "dependency-a")
      ;; But it doesn't actually upgrade anything.
      (should (string= stdout "dependency-a 1.0\n"))
      (should (= exit-code 0)))))


(ert-deftest eldev-upgrade-downgrade-1 ()
  (let ((eldev--test-project "project-h"))
    (eldev--test-delete-cache)
    (eldev--test-run nil ("version" "dependency-a")
      (should (string= stdout "dependency-a 1.0\n"))
      (should (= exit-code 0)))
    ;; Make sure that it doesn't change anything.
    (eldev--test-run nil ("upgrade" "--downgrade")
      (should (string= stdout "All dependencies are up-to-date\n"))
      (should (= exit-code 0)))))

(ert-deftest eldev-upgrade-downgrade-2 ()
  (let ((eldev--test-project "project-h"))
    (eldev--test-delete-cache)
    (eldev--test-run nil ("--unstable" "version" "dependency-a")
      (should (string= stdout "dependency-a 1.1\n"))
      (should (= exit-code 0)))
    (eldev--test-run nil ("upgrade" "--downgrade")
      (should (string= stdout "Upgraded or installed 1 dependency package\n"))
      (should (= exit-code 0)))
    (eldev--test-run nil ("version" "dependency-a")
      (should (string= stdout "dependency-a 1.0\n"))
      (should (= exit-code 0)))))

(ert-deftest eldev-upgrade-downgrade-3 ()
  (let ((eldev--test-project "project-h"))
    (eldev--test-delete-cache)
    (eldev--test-run nil ("--unstable" "version" "dependency-a")
      (should (string= stdout "dependency-a 1.1\n"))
      (should (= exit-code 0)))
    ;; Make sure that it is not confused by (unused) archive with a higher priority.
    (eldev--test-run nil ("--setup" `(eldev-use-package-archive '("archive-d" . ,(expand-file-name "../package-archive-d")) 300)
                          "upgrade" "--downgrade")
      (should (string= stdout "Upgraded or installed 1 dependency package\n"))
      (should (= exit-code 0)))
    (eldev--test-run nil ("version" "dependency-a")
      (should (string= stdout "dependency-a 1.0\n"))
      (should (= exit-code 0)))))


;; This is for e.g. upgrading Buttercup used for testing.
(ert-deftest eldev-upgrade-runtime-dependencies-1 ()
  (let ((eldev--test-project "project-a"))
    (eldev--test-delete-cache)
    (eldev--test-run nil ("--setup" `(eldev-add-extra-dependencies 'runtime 'dependency-b)
                          "version" "dependency-b")
      (should (string= stdout "dependency-b 1.0\n"))
      (should (= exit-code 0)))
    ;; Note that even if we order upgrading only `dependency-b', `dependency-a' also must
    ;; be upgraded, because `dependency-b 1.1' requires `dependency-a 1.1'.
    (eldev--test-run nil ("--setup" `(eldev-use-package-archive `("archive-b" . ,(expand-file-name "../package-archive-b")) 0)
                          "upgrade" "dependency-b")
      (should (string= stdout "Upgraded or installed 2 dependency packages\n"))
      (should (= exit-code 0)))
    (eldev--test-run nil ("version" "dependency-b")
      (should (string= stdout "dependency-b 1.1\n"))
      (should (= exit-code 0)))))


(ert-deftest eldev-upgrade-disabled-dependencies ()
  (let ((eldev--test-project "project-a"))
    (eldev--test-delete-cache)
    (eldev--test-run nil ("--disable-dependencies" "upgrade")
      (should (string-match-p "standard dependency management is disabled" stderr))
      (should (= exit-code 1)))))


(provide 'test/upgrade)
