penpot/common/test/common_tests/logging_test.cljc
Andrey Antukh 0ef35fc52a
🐛 Harden browser logging against invalid levels (#11693)
* 🐛 Stop browser logging from crashing on empty levels

Stop level->int crashes from taking down the dashboard when a
nil or unknown level reaches the browser logger. Invalid levels
now warn and are ignored in enabled?, setup! and the console
handler, which renders unknown records with a neutral fallback.

Alias the schema-legal :fatal level to :error in the browser
mappings and validate the JS-exported debug.set_logging, which
previously threw on missing arguments and wrote unreachable
keyword keys into the loggers map.

Closes #11690

AI-assisted-by: muse-spark-1.3-contributor

* 🐛 Guard logger args and strengthen logging tests

Close the residual throw paths next to the empty-level crash:
guard non-string loggers in enabled? and setup!, coerce
set_logging arguments safely, and validate logger keys.

Strengthen the regression tests so the fatal alias cannot
regress silently: enabled-logger filtering, JVM fatal and bogus
cases, setup! skip proof, and invalid-logger cases.

Related to #11690

AI-assisted-by: muse-spark-1.3-contributor

* 🐛 Address low findings from logging review

Validate the logger before the level in console-log-handler,
share a public valid-logger? predicate with debug/set-logging,
and keep warn formatting consistent across boundaries.

Document the fail-soft-FE/strict-BE split on enabled? and the
valid-level? contract on set-level!. Cover safe fallbacks, bad
logger keys, handler logger skips, and loggers-map isolation in
common tests, and add a frontend test for debug/set-logging.

Related to #11690

AI-assisted-by: muse-spark-1.3-contributor
2026-09-30 07:17:02 +02:00

102 lines
4.3 KiB
Clojure

;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS INC Sucursal en España SL
(ns common-tests.logging-test
(:require
[app.common.logging :as l]
[clojure.test :as t]))
(defn- throws?
[thunk]
#?(:clj (try (thunk) false (catch clojure.lang.ExceptionInfo _ true))
:cljs (try (thunk) false (catch :default _ true))))
(t/deftest level->int-test
(t/is (= 10 (l/level->int :trace)))
(t/is (= 20 (l/level->int :debug)))
(t/is (= 30 (l/level->int :info)))
(t/is (= 40 (l/level->int :warn)))
(t/is (= 50 (l/level->int :error)))
(t/is (= 50 (l/level->int :fatal)))
(t/is (throws? #(l/level->int nil)))
(t/is (throws? #(l/level->int :bogus))))
#?(:cljs
(t/use-fixtures
:each
(fn [f]
(f)
(doseq [k ["logging-test-probe-xyz"
"logging-test-fatal-xyz"
"logging-test-setup-xyz"
"logging-test-bad-xyz"
"logging-test-key-xyz"]]
(.delete l/loggers k)))))
#?(:cljs
(t/deftest browser-boundaries-test
(t/testing "unknown levels never throw and disable logging"
(t/is (false? (l/enabled? "logging-test-probe-xyz" nil)))
(t/is (false? (l/enabled? "logging-test-probe-xyz" :bogus))))
(t/testing "invalid loggers never throw and disable logging"
(t/is (false? (l/enabled? nil :error)))
(t/is (false? (l/enabled? "" :error)))
(t/is (false? (l/enabled? 123 :error))))
(t/testing "unknown logger defaults to disabled"
(t/is (false? (l/enabled? "logging-test-probe-xyz" :fatal)))
(t/is (false? (l/enabled? "logging-test-probe-xyz" :error))))
(t/testing "fatal filters exactly like error when enabled"
(l/setup! {"logging-test-fatal-xyz" :debug})
(t/is (true? (l/enabled? "logging-test-fatal-xyz" :fatal)))
(t/is (= (l/enabled? "logging-test-fatal-xyz" :fatal)
(l/enabled? "logging-test-fatal-xyz" :error)))
(l/setup! {"logging-test-fatal-xyz" :error})
(t/is (true? (l/enabled? "logging-test-fatal-xyz" :fatal)))
(t/is (false? (l/enabled? "logging-test-fatal-xyz" :debug)))
(t/is (false? (l/enabled? "logging-test-fatal-xyz" :trace))))
(t/testing "setup! skips invalid entries and installs valid ones"
(t/is (do (l/setup! {"logging-test-setup-xyz" :error
"logging-test-bad-xyz" nil})
true))
(t/is (true? (l/enabled? "logging-test-setup-xyz" :error)))
(t/is (false? (l/enabled? "logging-test-setup-xyz" :bogus)))
(t/is (false? (l/enabled? "logging-test-bad-xyz" :error))))
(t/testing "setup! skips invalid logger keys and installs valid ones"
(t/is (do (l/setup! {"logging-test-key-xyz" :error
"" :error
nil :error})
true))
(t/is (true? (l/enabled? "logging-test-key-xyz" :error))))
(t/testing "safe fallbacks never throw"
(t/is (= "#969896" (l/level->color-safe nil)))
(t/is (= "UNK" (l/level->name-safe nil)))
(t/is (= "#c82829" (l/level->color-safe :fatal)))
(t/is (= "ERR" (l/level->name-safe :fatal))))
(t/testing "console handler survives a nil-level record"
(t/is (nil? (l/console-log-handler
nil nil nil
{::l/logger "logging-test-probe-xyz"
::l/level nil
::l/message (delay "hi")
::l/props {}}))))
(t/testing "console handler skips invalid-logger records"
(t/is (nil? (l/console-log-handler
nil nil nil
{::l/logger nil
::l/level nil
::l/message (delay "hi")
::l/props {}}))))))
#?(:clj
(t/deftest backend-strict-test
(t/testing "fatal does not throw"
(t/is (do (l/enabled? "app" :fatal) true)))
(t/testing "JVM branch still rejects invalid levels loudly"
(t/is (try (l/enabled? "app" nil) false
(catch IllegalArgumentException _ true)))
(t/is (try (l/enabled? "app" :bogus) false
(catch IllegalArgumentException _ true))))))