Compare commits

...

50 Commits

Author SHA1 Message Date
bryan
aae19be618 Merge branch 'master' into claude-code-clojure 2025-04-18 16:45:55 -06:00
bryan
407dd14106 Merge branch 'master' into claude-code-clojure 2025-04-18 15:30:10 -06:00
Bryan Maass
3a8184b185 introduce the no-issue-links label 2025-04-18 13:49:43 -06:00
Bryan Maass
8d8a3e2a96 reenable the linter 2025-04-18 13:49:43 -06:00
Bryan Maass
7821b79043 see where turning off the linter gets us 2025-04-18 13:49:43 -06:00
Bryan Maass
d28ff2f121 [ci nocache] switch back the original script 2025-04-18 13:49:43 -06:00
Bryan Maass
d02f02ee0b what is going on 2025-04-18 13:49:43 -06:00
Bryan Maass
e93e4e05c0 try busting some cache by switching to mage kondo 2025-04-18 13:49:43 -06:00
Bryan Maass
2eb994bd95 shuffle code around 2025-04-18 13:49:43 -06:00
Bryan Maass
e613f283db fix output + fix user.clj 2025-04-18 13:49:43 -06:00
Bryan Maass
4d888a1de4 cleanup -check-readable 2025-04-18 13:49:43 -06:00
Bryan Maass
f1f5dde5d8 -check-readable task naming 2025-04-18 13:49:43 -06:00
Bryan Maass
160bbec78d Refactor mage -eval to mage -repl with improved usability and reliability
The implementation uses eval and read-string strategically to control when and where code is evaluated:

1. The code from the command line must first be transported as a string to the running REPL server
2. read-string allows parsing the code without executing it
3. eval then executes the code in the correct namespace context
4. This creates a "REPL inside a REPL" where namespace switching and evaluation happen in a controlled environment
5. This approach prevents premature evaluation and ensures code runs in the intended scope
2025-04-18 13:49:43 -06:00
Bryan Maass
d934b0babf Update CLAUDE.md to replace -balance-parens-check with -code-is-readable 2025-04-18 13:49:43 -06:00
Bryan Maass
8686c53446 balanced-parens? -> is-readable-code?
- Returns explicit errors, usually with line numbers.
2025-04-18 13:49:43 -06:00
Bryan Maass
72001629e6 feat: enable equals-true Kondo linter and fix warnings
This change enables the `:equals-true` warning in the clj-kondo linter configuration,
which identifies code patterns where `(= true x)` is used instead of the more idiomatic
`(true? x)` form in Clojure.

Key changes:
- Enable `:equals-true` linter with warning level in .clj-kondo/config.edn
- Fix all instances of `(= true x)` throughout the codebase, replacing with `(true? x)`
- Add a new utility to check balanced parentheses in Clojure forms
- Add a new mage command `-balance-parens-check` to validate balanced parentheses
- Update CLAUDE.md with documentation about the balanced parentheses utility

This change improves code quality by promoting idiomatic Clojure usage and adds
a helpful tool for ensuring proper Clojure syntax when editing code.
2025-04-18 13:49:43 -06:00
Bryan Maass
5c845ba3c3 remove mcp stuff for now 2025-04-18 13:49:43 -06:00
Bryan Maass
3d42a5c78c Add solid instructions for calling into the repl 2025-04-18 13:49:43 -06:00
Bryan Maass
a638e855da [ci nocache] switch back the original script 2025-04-18 13:49:43 -06:00
Bryan Maass
a1b8c3b5b8 what is going on 2025-04-18 13:49:43 -06:00
Bryan Maass
e67b2d64fc try busting some cache by switching to mage kondo 2025-04-18 13:49:43 -06:00
Bryan Maass
e105a57db1 format testing string 2025-04-18 13:49:43 -06:00
Bryan Maass
ba1500863a cleanup .clj-kondo/config.edn 2025-04-18 13:49:43 -06:00
bryan
a51ab2900c Update CLAUDE.md
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-04-18 13:49:43 -06:00
Bryan Maass
e934b54f41 fix comment 2025-04-18 13:49:43 -06:00
Bryan Maass
328ebbcead fix typos + eval ::loaded when no code string 2025-04-18 13:49:43 -06:00
Bryan Maass
a5f4c4a190 reword claude.md 2025-04-18 13:49:43 -06:00
Bryan Maass
3848e58b39 shuffle code around 2025-04-18 13:49:43 -06:00
Bryan Maass
b8345f0176 fix output + fix user.clj 2025-04-18 13:49:43 -06:00
Bryan Maass
03f140aedc cleanup -check-readable 2025-04-18 13:49:43 -06:00
Bryan Maass
aabf32d9f2 -check-readable task naming 2025-04-18 13:49:43 -06:00
Bryan Maass
4c129b2d75 Refactor mage -eval to mage -repl with improved usability and reliability
The implementation uses eval and read-string strategically to control when and where code is evaluated:

1. The code from the command line must first be transported as a string to the running REPL server
2. read-string allows parsing the code without executing it
3. eval then executes the code in the correct namespace context
4. This creates a "REPL inside a REPL" where namespace switching and evaluation happen in a controlled environment
5. This approach prevents premature evaluation and ensures code runs in the intended scope
2025-04-18 13:49:43 -06:00
Bryan Maass
6bd5115ca3 Update CLAUDE.md to replace -balance-parens-check with -code-is-readable 2025-04-18 13:49:43 -06:00
Bryan Maass
d8dcb4c67e Fix kondo-updated when there are no matching files 2025-04-18 13:49:43 -06:00
Bryan Maass
e1f803afd1 update claude.md (mostly formatting) 2025-04-18 13:49:43 -06:00
Bryan Maass
1897a37ade balanced-parens? -> is-readable-code?
- Returns explicit errors, usually with line numbers.
2025-04-18 13:49:43 -06:00
Bryan Maass
472c718b70 update claude.md from github copliot's review 2025-04-18 13:49:43 -06:00
Bryan Maass
cfbb5b1197 move the linter to the things-we-use section 2025-04-18 13:49:43 -06:00
Bryan Maass
9b88ab8ed3 fix remaining (= true x) -> (true? x) linter warnings by adding clj-kondo ignores
We need the ignores for 'are' macro clj-kondo tests where (= expected (fn x)) is the idiomatic pattern.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-04-18 13:49:43 -06:00
Bryan Maass
d73c1f0581 fix more = true -> true? linter errors automatically 2025-04-18 13:49:43 -06:00
Bryan Maass
d423ca80a0 fix more, refine claude instructions 2025-04-18 13:49:43 -06:00
Bryan Maass
ac78a9f92d fix: update code to resolve equals-true Kondo linter warnings
This commit addresses the newly enabled equals-true linter warnings by:
- Adding #_{:clj-kondo/ignore [:equals-true]} to are forms that legitimately need to test both true/false values
- Replacing (= true x) with (true? x) and (= false x) with (false? x) in test assertions where appropriate

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-04-18 13:49:43 -06:00
Bryan Maass
556daad590 feat: enable equals-true Kondo linter and fix warnings
This change enables the `:equals-true` warning in the clj-kondo linter configuration,
which identifies code patterns where `(= true x)` is used instead of the more idiomatic
`(true? x)` form in Clojure.

Key changes:
- Enable `:equals-true` linter with warning level in .clj-kondo/config.edn
- Fix all instances of `(= true x)` throughout the codebase, replacing with `(true? x)`
- Add a new utility to check balanced parentheses in Clojure forms
- Add a new mage command `-balance-parens-check` to validate balanced parentheses
- Update CLAUDE.md with documentation about the balanced parentheses utility

This change improves code quality by promoting idiomatic Clojure usage and adds
a helpful tool for ensuring proper Clojure syntax when editing code.
2025-04-18 13:49:43 -06:00
Bryan Maass
26053e133b flesh out Lint PR quick command 2025-04-18 13:49:43 -06:00
Bryan Maass
4464be1a8b fix: Add missing docstrings in mage/be_dev.clj
Added docstrings to parse-int, nrepl-port, and nrepl-eval functions to fix clj-kondo linting warnings.
2025-04-18 13:49:43 -06:00
Bryan Maass
624c118990 add lint pr quick command 2025-04-18 13:49:43 -06:00
Bryan Maass
b0d4aea79d remove mcp stuff for now 2025-04-18 13:49:43 -06:00
Bryan Maass
7ff41f59eb teach it how to behave when there's no repl 2025-04-18 13:49:43 -06:00
Bryan Maass
b645d99b84 Add solid instructions for calling into the repl 2025-04-18 13:49:42 -06:00
Bryan Maass
2b6bc390b3 teach claude how to write backend code 2025-04-18 13:49:42 -06:00
81 changed files with 825 additions and 302 deletions

View File

@@ -11,6 +11,7 @@
:def-fn {:level :warning}
:dynamic-var-not-earmuffed {:level :warning}
:equals-expected-position {:level :warning, :only-in-test-assertion true}
:equals-true {:level :warning}
:keyword-binding {:level :warning}
:main-without-gen-class {:level :warning}
:minus-one {:level :warning}
@@ -42,7 +43,6 @@
#_:docstring-leading-trailing-whitespace
#_:equals-false
#_:equals-true
#_:used-underscored-binding
;;

126
CLAUDE.md
View File

@@ -12,6 +12,7 @@
## Quick Commands
### JavaScript/TypeScript
- **Lint:** `yarn lint-eslint`
- **Test:** `yarn test-unit path/to/file.unit.spec.js` or `yarn test-unit -t "pattern"`
- **Watch:** `yarn test-unit-watch path/to/file.unit.spec.js`
@@ -19,9 +20,132 @@
- **Type Check:** `yarn type-check`
### Clojure
- **Lint:** `./bin/mage kondo [path]`
- **Lint PR:** `./bin/mage kondo-updated master` (or whatever target branch)
- Call the command one time at the beginning, record the results, then work through the problems one at a time.
- If the solution is obvious, then please apply the fix. Otherwise skip it.
- If you fix all the issues (and verify by rerunning the kondo-updated command):
- commit the change with a succinct and descriptive commit message
- **Lint File:** `./bin/mage kondo <file or files>` (or whatever target branch)
- Use the linter as a way to know that you are adhering to conventions in place in the codebase
- **Lint Changes:** `./bin/mage kondo-updated HEAD`
- **Format:** `./bin/mage cljfmt-files [path]`
- **Test file:** `clojure -X:dev:test :only namespace/test-name`
- **Check Code Readability** `./bin/mage -check-readable` with optional line-number
- Run this after every change to Clojure code, only accept readable code
- **Evaluating Clojure Code** `./bin/mage -repl '<code>'`
- See `Sending code to the REPL` for more details
### ClojureScript
- **Test:** `yarn test-cljs`
## Clojure REPL-driven development
- Start with small, fundamental functions:
- Identify the core features or functionalities required for your task.
- Break each feature down into the smallest, most basic functions that can be developed and tested independently.
- Write and test in the REPL:
- Write the code for each small function directly in the REPL (Read-Eval-Print Loop).
- Test it thoroughly with a variety of inputs, including typical use cases and relevant edge cases, to ensure it
behaves as expected.
- Integrate into source code:
- Once a function works correctly in the REPL, move it from the REPL environment into your source code files (e.g.,
within appropriate namespaces).
- Gradually increase complexity:
- Build upon tested, basic functions to create more complex functions or components.
- Compose smaller functions together, testing each new composition in the REPL to verify correctness step by step.
- Ensure dependency testing:
- Make sure every function is fully tested in the REPL before it is depended upon by other functions.
- This ensures that each layer of your application is reliable before you build on it.
- Leverage the REPL fully:
- Use the REPL as your primary tool to experiment with different approaches, iterate quickly, and get immediate
feedback on your code.
- Follow functional programming principles:
- Keep functions small, focused, and composable.
- Leverage Clojure's functional programming features—like immutability, higher-order functions, and the standard
library—to write concise, effective code.
### How to evaluate code
#### Keeping Code Readable
The `./bin/mage -check-readable <file> <optional: line-number>` command checks if your Clojure code can be properly parsed.
This ensures your changes maintain valid syntax and structure.
- Edit Clojure files one step at a time.
- After EVERY change to a Clojure form, call `mage -check-readable src/metabase/thefile.clj <line-number>` with the line number.
- If it's readable then call `mage -check-readable dev/src/dev.clj` without the line number to check the entire file.
- If the change results in unreadable code, try again until it is readable.
- To overcome errors about parens, pay close attention to them. Count opening/closing parens you add/remove.
#### Bottom-up dev loop
1. Write code into a file.
2. Evaluate the file's namespace and make sure it loads correctly with:
```
mage -repl --namespace metabase.db.connection
```
3. Call functions in the namespace with test inputs, and observe that the outputs are correct 3.1
Feel free to copy these REPL session trials into actual test cases using `deftest` and `is`.
4. Once you know these functions are good, return to 1, and compose them into the task that you need to build.
#### Sending code to the REPL
- Send code to the metabase process REPL using: `./bin/mage -repl '(+ 1 1)'` where `(+ 1 1)` is your Clojure code.
- See `./bin/mage -repl -h` for more details.
- If the Metabase backend is not running, you'll see an error message with instructions on how to start it.
##### Working with files and namespaces
1. **Load a file and call functions with fully qualified names**:
To call `your.namespace/your-function` on `arg1` and `arg2`:
```
./bin/mage -repl --namespace your.namespace '(your-function arg1 arg2)'
```
DO NOT use "require", "load-file" etc in the code string argument.
##### Understanding the response
The `./bin/mage -repl` command returns three separate, independent outputs:
- `value`: The return value of the last expression (best for data structures)
- `stdout`: Any printed output from `println` etc. (best for messages)
- `stderr`: Any error messages (best for warnings and errors)
Example call:
```bash
./bin/mage -repl '(println "Hello, world!") '\''({0 1, 1 3, 2 0, 3 2} {0 2, 1 0, 2 3, 3 1})'
```
Example response:
```
ns: user
session: 32a35206-871c-4553-9bc9-f49491173d1c
value: ({0 1, 1 3, 2 0, 3 2} {0 2, 1 0, 2 3, 3 1})
stdout: Hello, world!
stderr:
```
For effective REPL usage:
- Return data structures as function return values
- Use `println` for human-readable messages
- Print errors to stderr
## Tips
- End all files with a newline.
- When editing tabular code, where the columns line up, try to keep them aligned.
## Critical REPL Usage Rules
- Be careful with parentheses counts when editing Clojure code
- After EVERY change to Clojure code, verify readability with `-check-readable`

51
bb.edn
View File

@@ -126,6 +126,57 @@
;; - hidden from `./bin/mage` listing and `bb tasks`
;; - start with a `-`
-repl
{:doc "Eval a string of clojure code in the backend dev server"
:requires [[babashka.process :as process]
[mage.color :as c]
[mage.be-dev :refer [nrepl-eval]]]
:examples [["./bin/mage -repl '(+ 1 1)'"
"Eval a form in the backend dev server in the user namespace"]
["./bin/mage -repl --namespace metabase.db.connection @application-db-counter"
"Eval a form in the backend dev server in the 'metabase.db.connection' namespace"]
["mage -repl --port 59498 --namespace metabase.db.connection '(read-string \"::hello\")'"
"Evaluate a keyword inside a namespace"]]
:options [["-n" "--namespace NAMESPACE" "Namespace to use for the task"]
["-p" "--port PORT" "Port to use for the task, defaults to value in .nrepl-port"]]
:task (let [parsed (cli/parse! (current-task))
code (first (:arguments parsed))
port (or (:port (:options parsed))
(slurp ".nrepl-port")
(do (println (c/red "No port specified, and no .nrepl-port file found."))
(System/exit 1)))
nns (or (:namespace (:options parsed)) "user")]
(nrepl-eval nns code port))}
-check-readable
{:doc "Check that code is readable parens a file"
:requires [[mage.readability-check :as readability]]
:arg-schema [:or
[:tuple [:string {:description "File to check"}]]
[:tuple
[:string {:description "File to check"}]
[:int {:description "A line in the file" :min 1}]]]
:examples [["./bin/mage -check-readable dev/src/dev.clj"
"Check file for readability"]
["./bin/mage -check-readable dev/src/dev.clj 300"
"Check top level form containing line 300 for readability"]]
:task (let [{:keys [arguments]} (cli/parse! (current-task))]
(apply readability/check arguments))}
-code-is-readable
{:doc "Check for unbalanced parens in the current file, given a starting line and ending line"
:requires [[mage.be-dev :refer [balanced-parens?]]]
:arg-schema [:or [:tuple [:string {:description "File to check"}]]
[:tuple
[:string {:description "File to check"}]
[:int {:description "A line in the file" :min 1}]]]
:examples [["./bin/mage -check-readable dev/src/dev.clj"
"Check file for readability"]
["./bin/mage -check-readable dev/src/dev.clj 300"
"Check top level form containing line 300 for readability"]]
:task (let [{:keys [arguments]} (cli/parse! (current-task))]
(apply readability-check arguments))}
-example-calculator
{;; `:doc` is a short docstring for the task, will be listed in `./bin/mage -h` and other help info
:doc "The sample task docstring"

View File

@@ -26,6 +26,7 @@
(deftest ^:parallel backend-message?
(testing "messages present in any .clj and .cljc files are detected as backend messages"
#_{:clj-kondo/ignore [:equals-true]}
(are [source-references expected] (= expected
(@#'backend/backend-message? {:source-references source-references}))
;; Simple .clj and .cljc files with and without line numbers

View File

@@ -65,8 +65,7 @@
(defn privatize-symbol! [[symb {:keys [analysis]}]]
(lsp-cmd/call-command
:cycle-privacy
{:loc "?"
:db}))
{:loc "?" :db "?"}))
(comment

View File

@@ -36,4 +36,4 @@
(is (= [{::just-a-fake-cached-hydration-function!!! :cached}]
(t2/hydrate [{}] ::just-a-fake-cached-hydration-function!!!))))
(testing "The 'cache' was populated"
(is (= true @*cached?*)))))
(is (true? @*cached?*)))))

View File

@@ -182,7 +182,7 @@
(run-ad-hoc-query))))
(testing "sanity check: should be able to run query as saved Question before block perms are set."
(is (run-saved-question))
(is (= true (check-block-perms))))
(is (true? (check-block-perms))))
;; 'grant' the block permissions.
(testing "the highest permission level from any group wins (block doesn't override other groups anymore)"
(data-perms/set-database-permission! group-id (mt/id) :perms/view-data :blocked)
@@ -196,7 +196,7 @@
(run-ad-hoc-query))))
(testing "should STILL be able to run query as saved Question"
(is (run-saved-question))
(is (= true (check-block-perms)))))
(is (true? (check-block-perms)))))
(testing "once blocked in all groups, now access is truly blocked"
(data-perms/set-database-permission! (perms-group/all-users) (mt/id) :perms/view-data :blocked)
(testing "disallow running the query"

View File

@@ -16,5 +16,4 @@
(testing "can be set to `true` when `ee-openai-api-key` is set."
(llm.settings/ee-openai-api-key! "TOTALLY_REAL_KEY")
(llm.settings/ee-ai-features-enabled! true)
(is (= true
(llm.settings/ee-ai-features-enabled))))))))
(is (true? (llm.settings/ee-ai-features-enabled))))))))

View File

@@ -766,8 +766,8 @@
(testing "Run it again, should be cached"
(let [result (run-query)]
(is (= true
(:cached (:cache/details result))))
(is (true?
(:cached (:cache/details result))))
(is (= [[10]]
(mt/rows result)))))
(testing "Run the query with different User attributes, should not get the cached result"

View File

@@ -250,7 +250,7 @@
:value "True"}]}
response (scim-client :patch 200 (format "ee/scim/v2/Users/%s" entity-id) patch-body)]
(is (malli= scim-api/SCIMUser response))
(is (= true (:active response)))))
(is (true? (:active response)))))
(testing "Update family name of an existing user"
(let [patch-body {:schemas ["urn:ietf:params:scim:api:messages:2.0:PatchOp"]
@@ -277,7 +277,7 @@
(is (malli= scim-api/SCIMUser response))
(is (= "UpdatedFirstName" (get-in response [:name :givenName])))
(is (= "UpdatedLastName" (get-in response [:name :familyName])))
(is (= true (response :active)))))
(is (true? (response :active)))))
(testing "Error when using unsupported path"
(let [patch-body {:schemas ["urn:ietf:params:scim:api:messages:2.0:PatchOp"]

View File

@@ -119,5 +119,5 @@
(test-select-identical model))))
(deftest has-post-insert?-test
(is (= true
(#'upsert/has-post-insert? :model/User))))
(is (true?
(#'upsert/has-post-insert? :model/User))))

View File

@@ -29,7 +29,7 @@
(is (= nil
(entity-id)))
(testing "Should return truthy on success"
(is (= true (v2.entity-ids/seed-entity-ids!))))
(is (true? (v2.entity-ids/seed-entity-ids!))))
(is (= "998b109c"
(entity-id))))
(testing "Error: duplicate entity IDs"
@@ -70,8 +70,8 @@
(some-> (t2/select-one-fn :entity_id :model/Collection :id (:id c)) str/trim))]
(is (some? (entity-id)))
(testing "Should return truthy on success"
(is (= true
(v2.entity-ids/drop-entity-ids!))))
(is (true?
(v2.entity-ids/drop-entity-ids!))))
(is (nil? (entity-id))))))))
(testing "empty table"
(testing "has no entity ids"
@@ -80,8 +80,8 @@
:slug "no_entity_id_collection"}]
(is (nil? (t2/select-fn-set :entity-id :model/Dashboard)))
(testing "but doesn't crash drop-entity-ids"
(is (= true
(v2.entity-ids/drop-entity-ids!)))
(is (true?
(v2.entity-ids/drop-entity-ids!)))
(is (nil? (t2/select-fn-set :entity-id :model/Dashboard))))))))))
(deftest entity-ids-are-nullable

View File

@@ -25,8 +25,8 @@
(mt/with-temp [:model/NativeQuerySnippet snippet {:collection_id (:id collection)}]
(testing "\nShould be allowed regardless if EE features aren't enabled"
(mt/with-premium-features #{}
(is (= true
(has-perms? snippet))
(is (true?
(has-perms? snippet))
"allowed?")))
(testing "\nWith EE features enabled"
(mt/with-premium-features #{:snippet-collections}
@@ -45,8 +45,8 @@
"allowed?"))
(perms/grant-collection-readwrite-permissions! (perms-group/all-users) collection)
(testing (format "\nShould be allowed if we have write perms for %s" collection-name)
(is (= true
(has-perms? snippet))
(is (true?
(has-perms? snippet))
"allowed?"))))))))))
(deftest list-test
@@ -118,8 +118,8 @@
(testing (format "\nMove from %s -> %s should need write ('curate') perms for both" (:name source-collection) (:name dest-collection))
(testing "\nShould be allowed if EE perms aren't enabled"
(mt/with-premium-features #{}
(is (= true
(has-perms?)))))
(is (true?
(has-perms?)))))
(mt/with-premium-features #{:snippet-collections}
(doseq [c [source-collection dest-collection]]
(testing (format "\nPerms for only %s should fail" (:name c))
@@ -133,8 +133,8 @@
(try
(doseq [c [source-collection dest-collection]]
(perms/grant-collection-readwrite-permissions! (perms-group/all-users) c))
(is (= true
(has-perms?)))
(is (true?
(has-perms?)))
(finally
(doseq [c [source-collection dest-collection]]
(perms/revoke-collection-permissions! (perms-group/all-users) c)))))))))))))))))

121
mage/mage/be_dev.clj Normal file
View File

@@ -0,0 +1,121 @@
(ns mage.be-dev
(:require
[bencode.core :as bencode]
#_:clj-kondo/ignore
[clojure.pprint :as pp]
[clojure.string :as str]
[mage.color :as c]
[mage.util :as u]))
(set! *warn-on-reflection* true)
(defn- safe-parse-int
"Parse a string into an integer. Returns nil if parsing fails. Returns the input unchanged if not a string."
[s-or-i]
(if (string? s-or-i)
(let [s (str/trim s-or-i)]
(try
(Integer/parseInt s)
(catch Exception _ nil)))
s-or-i))
(defn- print-capture-code
"Capture output and return it as strings along with the value from the orignal code."
[code-string]
(str "
(let [o# (new java.io.StringWriter)
e# (new java.io.StringWriter)]
(binding [*out* o#
*err* e#]
{:value (do" code-string ")
:stdout (str o#)
:stderr (str e#)}))"))
(defn eval-in-ns
"This insane code evals the code in the proper namespace.
It's basically a repl inside a repl."
[nns code]
(str "
(let [ns-sym (symbol \"" nns "\")]
(require ns-sym :reload)
(in-ns ns-sym)
(eval (read-string " (pr-str (or code "::loaded")) ")))"))
(defn- bootstrap-code
"Capture output and return it as strings along with the value from the orignal code."
[code-string]
(str "
(let [o# (new java.io.StringWriter)
e# (new java.io.StringWriter)]
(binding [*out* o#
*err* e#]
{:value (do" code-string ")
:stdout (str o#)
:stderr (str e#)}))"))
(defn eval-in-ns
"This insane code evals the code in the proper namespace.
It's basically a repl inside a repl."
[nns code]
(str "
(let [ns-sym (symbol \"" nns "\")]
(require ns-sym :reload)
(in-ns ns-sym)
(eval (read-string " (pr-str (or code "::no-op")) ")))"))
(defn nrepl-eval
"Evaluate Clojure code in a running nREPL server. With one arg, reads port from .nrepl-port file.
With two args, uses the provided port number. Returns and formats the evaluation results."
([nns code]
(nrepl-eval (or nns "user") code (slurp ".nrepl-port")))
([nns code port]
(try (let [port (safe-parse-int port)
s (java.net.Socket. "localhost" port)
out (.getOutputStream s)
in (java.io.PushbackInputStream. (.getInputStream s))
code-str (->> code
(eval-in-ns nns)
print-capture-code)
_ (u/debug "Code: ----- \n" code-str "\n -----")
_ (bencode/write-bencode out {"op" "eval" "code" code-str})
return (update-vals (bencode/read-bencode in) slurp)]
(doseq [[k v] return]
(if (= k "value")
(if-let [v (try (read-string v)
;; try to read v, which is a map but comes back as a string:
(catch Exception _ nil))]
(do
(println "value: " (pr-str (:value v)))
(when-not (str/blank? (:stdout v))
(println "stdout: ")
(doseq [out (str/split-lines (:stdout v))]
(println " " out)))
(when-not (str/blank? (:stderr v))
(println "stderr: ")
(doseq [err (str/split-lines (:stderr v))]
(println " " err))))
(println "value: " v))
(println (str k ":") v))))
(catch java.net.ConnectException _
(println (str "Could not connect to the REPL server on port: " (c/red port) " (found port number in .nrepl-port).\n"
"Is the Metabase backend running?\n\n"
"To start it, run:\n"
(c/green " clj -M:dev:ee:ee-dev:drivers:drivers-dev:dev-start\n\n")
"If you prefer a different way to start the backend, please use that instead.")))
(catch Exception e
(if (= "No matching ctor found for class java.net.Socket" (ex-message e))
(println (str "Unable to connect to nREPL server. Is it running on port " (c/yellow port) "?"))
(do
(println "message: " (ex-message e))
(println "data: " (pr-str (ex-data e)))
{:exception true
:message (ex-message e)
:data (ex-data e)}))))))
(comment
(nrepl-eval "metabase.logger-test" "(do (println :!! hi) hi)" 59498)
(nrepl-eval "metabase.logger-test" "*ns*" 59498)
(println code-str))

View File

@@ -30,7 +30,8 @@
(defn- coerce-arguments [arg-schema current-task arguments]
(if-not arg-schema
arguments
(let [decoded-args (mc/decode arg-schema arguments mtx/string-transformer)]
(let [decoded-args (try (mc/decode arg-schema arguments mtx/string-transformer)
(catch Exception e arguments))]
#_:clj-kondo/ignore ;; TODO: don't run all linters on these files
(if (mc/validate arg-schema decoded-args)
decoded-args
@@ -69,16 +70,20 @@
See: https://clojure.github.io/tools.cli/index.html#clojure.tools.cli/parse-opts"
[{:keys [options arg-schema] :as current-task}]
(check-print-help current-task)
(let [*error-hit? (atom false)
{:keys [summary]
option-errors :errors
:as parsed-opts} (tools.cli/parse-opts *command-line-args* options)
_ (check-option-errors option-errors *error-hit? summary)
parsed (update parsed-opts :arguments (partial coerce-arguments arg-schema current-task))]
(u/debug (c/green "UNPARSED: ") *command-line-args*)
(u/debug (c/green "PARSED: ") parsed)
parsed))
(try
(check-print-help current-task)
(let [*error-hit? (atom false)
{:keys [summary]
option-errors :errors
:as parsed-opts} (tools.cli/parse-opts *command-line-args* options)
_ (check-option-errors option-errors *error-hit? summary)
parsed (update parsed-opts :arguments (partial coerce-arguments arg-schema current-task))]
(u/debug (c/green "UNPARSED: ") *command-line-args*)
(u/debug (c/green "PARSED: ") parsed)
parsed)
(catch Exception e
(println (c/red "Error: " (.getMessage e)))
(System/exit 1))))
(comment

View File

@@ -0,0 +1,183 @@
(ns mage.readability-check
(:require
[babashka.fs :as fs]
[clojure.pprint :as pp]
[clojure.string :as str]
[edamame.core :as edamame]
[flatland.ordered.map :as m]))
(defn- find-data-readers []
(let [reader-tags (conj (keys (read-string
(slurp "resources/data_readers.clj")))
'js)]
(zipmap reader-tags (map (fn [tag]
#_:clj-kondo/ignore
(eval `(fn [v] (list (quote ~tag) v))))
reader-tags))))
(defn- can-read-content? [s]
(try
(edamame/parse-string-all s {:all true
:map m/ordered-map
:auto-resolve name
:features #{:cljs :bb}
:read-cond :allow
:readers (find-data-readers)
:eof ::done
:row-key :line
:col-key :column})
{:readable true}
(catch Exception e
{:readable false
:reason (ex-message e)
:data (ex-data e)})))
(defn- top-level-form-chunk [content]
(let [next-int! (let [*i (atom 0)] (fn [] (swap! *i inc)))]
(reduce
(fn [acc [line next-line]]
;; (prn ["last element:" (get acc (dec (count acc)))])
(if (and (= "" line) (str/starts-with? next-line "("))
(conj acc [(next-int!)])
;; update last element:
(update acc (dec (count acc)) conj (next-int!))))
[[]]
(partition-all 2 1 (str/split-lines content)))))
(defn- line-bounds [content line-number]
(let [top-level-chunks (top-level-form-chunk content)]
(if-let [hit (first (filter (fn [chunk] (<= (first chunk) line-number (last chunk)))
top-level-chunks))]
{:start (inc (first hit))
:end (inc (last hit))}
(throw (ex-info "Line number not found in any top-level form." {:cause :line-number-not-found})))))
(comment
(mapv
(juxt identity
#(line-bounds
(str/join "\n" ;; line
["(ns)" ;; 1
"" ;; 2
"(defn square [x] (* x x))" ;; 3
"" ;; 4
"(defn cube [x] " ;; 5
" (* x x x))" ;; 6
"" ;; 7
])
%))
[1 3 4 5])
;; => [[1 {:start 1, :end 2}]
;; [3 {:start 2, :end 4}]
;; [4 {:start 4, :end 7}]
;; [5 {:start 4, :end 7}]]
)
(defn- read-check-problem [reason]
(println "There was a problem with the command: " reason)
(System/exit 0))
(defn- report-chunk
[corpus line-number start end]
(let [linums (map str (range start (inc end)))
maxline-len (apply max (map count linums))
divider (str (str/join (repeat (inc maxline-len) "-")) "+")]
(println (str "Checking chunk containing line " line-number ": \n" divider))
(doseq [[linum line] (map vector linums corpus)]
(println (format (str "%-" maxline-len "s") linum) "|" line))
(println divider)))
(defn check
"Check if the file has balanced parens, and can be read by the Clojure reader.
Emulation of the Clojure reader, is done by edamame, which has been configured to work with our codebase.
If line-number is not provided, checks the whole file.
If line-number is provided, checks the top level form file which contains line-number."
[file & [line-number]]
(let [content (try (slurp file) (catch Exception _ (read-check-problem :missing-file)))]
(if-not line-number
#_:clj-kondo/ignore
(let [result (can-read-content? content)]
(prn result)
result)
(try (let [line-number (try (if (int? line-number) line-number (Integer/parseInt line-number))
(catch Exception _ (read-check-problem :invalid-line-number)))
lines (str/split-lines content)
line-count (count lines)
_ (when (> line-number line-count)
(throw (ex-info (str "Line number (" line-number ") is greater than the number of lines in the file (" line-count ").")
{:cause :line-number-too-high})))
{:keys [start end]} (line-bounds content line-number)
start (min start line-number)
end (max end line-number)
corpus (take (- end start) (drop (dec start) lines))]
(report-chunk corpus line-number start end)
(let [result (assoc
(can-read-content? (str/join "\n" (concat
;; fix edamame reporting by padding missing lines:
(repeat (dec start) "")
corpus)))
:starting-at (nth lines (dec start))
:ending-at (nth lines (dec (dec end))))]
#_:clj-kondo/ignore
(pp/pprint result)
result))
(catch Exception e
(let [data (ex-data e)]
(println "message: " (ex-message e))
(println "data: " (pr-str data))
{:readable false
:exception true
:message (ex-message e)
:data data}))))))
(comment ;; hi self
(check "test/metabase/models/card_test.clj" 20)
;; => {:readable true, :starting-at "(ns metabase.models.card-test\n (:require"}
(check "test/metabase/models/card_test.clj" 20000)
;; => {:readable false, :exception true,
;; :message "Line number 20000 is greater than the number of lines in the file (1472).",
;; :data {:cause :line-number-too-high}}
(can-read-content? (str/join "\n" ["[" "[" "}" "]" "]"]))
;; => {:readable false, :reason "Unmatched delimiter: }, expected: ] to match [ at [2 1]",
;; :data {:type :edamame/error, :line 3, :column 1, :edamame/opened-delimiter "[",
;; :edamame/opened-delimiter-loc {:row 2, :col 1}, :edamame/expected-delimiter "]"}}
(can-read-content? "[[[]] ")
;; => {:readable false, :reason "EOF while reading, expected ] to match [ at [1,1]",
;; :data {:type :edamame/error, :line 1, :column 7, :edamame/expected-delimiter "]",
;; :edamame/opened-delimiter "[", :edamame/opened-delimiter-loc {:row 1, :col 1}}}
(can-read-content? "1a")
;; => {:readable false, :reason "Invalid number: 1a", :data {:type :edamame/error, :line 1, :column 2}}
(check "mage/mage/readability_check.clj" 160)
;; => {:readable true, :starting-at "(comment ;; hi self", :ending-at " (= (read-all-files) #{{:readable true}}))"}
;soo meta :) :| :(
;; these cannot trip it up:
")" "}]" #inst "2020" @(atom 1) #_\) \) #_#_#_a a a
"uncomment this to try, it works, but is unreadable in bb:" ;;#something-cool 13
(check "bin/build/test/i18n/create_artifacts/backend_test.clj" 29)
;; => {:readable true, :starting-at "\n(deftest ^:parallel backend-message?"}
;; this should be a test:
(require '[babashka.fs :as fs])
(defn read-all-files []
(set
(keep (fn [f] (let [result (check f)] (when-not (:readable result) [f result])))
(str/split-lines (str/join "\n"
(concat (fs/glob "." "**/*.clj")
(fs/glob "." "**/*.cljc")
(fs/glob "." "**/*.cljs")))))))
;; run all files in the current directory and subdirectories, including in /jars:
;; filtering out readable ones:
(= (read-all-files) #{{:readable true}}))

View File

@@ -101,6 +101,7 @@
:out
(str/split-lines)
;; filter out any files that have been deleted/moved
(remove #{""})
(filter (fn [filename]
(fs/exists? (str project-root-directory "/" filename)))))))

View File

@@ -78,6 +78,6 @@
druid.client/GET (fn [_url & params] (vreset! get-args (apply hash-map params)) nil)]
;; Just fill in the params with internally modified `dbms-version`.
(druid.sync/dbms-version db-with-auth-details)
(is (= true (:auth-enabled @get-args)))
(is (true? (:auth-enabled @get-args)))
(is (= "admin" (:auth-username @get-args)))
(is (= "password1" (:auth-token-value @get-args)))))))))

View File

@@ -10,6 +10,7 @@
[metabase.api.common.validation :as validation]
[metabase.api.embed.common :as api.embed.common]
[metabase.api.macros :as api.macros]
[metabase.api.open-api :as open-api]
[metabase.config :as config]
[metabase.db :as mdb]
[metabase.driver :as driver]
@@ -133,3 +134,12 @@
{:keys [entity_ids]} :- [:map
[:entity_ids :map]]]
{:entity_ids (api.embed.common/model->entity-ids->ids entity_ids)})
(api.macros/defendpoint :get "/openapi"
"Return the OpenAPI specification for the Metabase API."
[]
(api/check-superuser)
{:status 200
:body (merge
(open-api/root-open-api-object @(requiring-resolve 'metabase.api.routes/routes))
{:servers [{:url "" :description "Metabase API"}]})})

View File

@@ -83,7 +83,7 @@
yaml/parse-string))
(defn- init-plugin-with-info!
"Initiaize plugin using parsed info from a plugin maifest. Returns truthy if plugin was successfully initialized;
"Initialize plugin using parsed info from a plugin manifest. Returns truthy if plugin was successfully initialized;
falsey otherwise."
[info]
(plugins.init/init-plugin-with-info! info))
@@ -163,7 +163,7 @@
(try
(init-plugin! path)
(catch Throwable e
(log/errorf e "Failied to initialize plugin %s" (.getFileName path))))))
(log/errorf e "Failed to initialize plugin %s" (.getFileName path))))))
(defn- load! []
(log/infof "Loading plugins in %s..." (str (plugins-dir)))

View File

@@ -247,7 +247,7 @@
(defmacro ^:private is-ex-data [expected-schema actual-call]
`(try
~actual-call
(is (= true false))
(is (true? false))
(catch clojure.lang.ExceptionInfo e#
(is (~'=?
~expected-schema

View File

@@ -41,33 +41,20 @@
(boolean (#'insights/valid-period? (inst->day from) (inst->day to) period))))
(deftest valid-period-test
(is (= true
(valid-period? #t "2015-01" #t "2015-02")))
(is (true? (valid-period? #t "2015-01" #t "2015-02")))
; Do we correctly handle descending time series?
(is (= true
(valid-period? #t "2015-02" #t "2015-01")))
(is (= true
(valid-period? #t "2015-02" #t "2015-03")))
(is (= false
(valid-period? #t "2015-01" #t "2015-03")))
(is (= false
(valid-period? #t "2015-01" nil)))
(is (= true
(valid-period? #t "2015-01-01" #t "2015-01-02")))
(is (= true
(valid-period? #t "2015-01-01" #t "2015-01-08")))
(is (= true
(valid-period? #t "2015-01-01" #t "2015-04-03")))
(is (= true
(valid-period? #t "2015" #t "2016")))
(is (= false
(valid-period? #t "2015-01-01" #t "2015-01-09")))
(is (= true
(valid-period? #t "2015-01-01" #t "2015-04-03" :quarter)))
(is (= false
(valid-period? #t "2015-01-01" #t "2015-04-03" :month)))
(is (= false
(valid-period? #t "2015-01" #t "2015-02" nil))))
(is (true? (valid-period? #t "2015-02" #t "2015-01")))
(is (true? (valid-period? #t "2015-02" #t "2015-03")))
(is (= false (valid-period? #t "2015-01" #t "2015-03")))
(is (= false (valid-period? #t "2015-01" nil)))
(is (true? (valid-period? #t "2015-01-01" #t "2015-01-02")))
(is (true? (valid-period? #t "2015-01-01" #t "2015-01-08")))
(is (true? (valid-period? #t "2015-01-01" #t "2015-04-03")))
(is (true? (valid-period? #t "2015" #t "2016")))
(is (= false (valid-period? #t "2015-01-01" #t "2015-01-09")))
(is (true? (valid-period? #t "2015-01-01" #t "2015-04-03" :quarter)))
(is (= false (valid-period? #t "2015-01-01" #t "2015-04-03" :month)))
(is (= false (valid-period? #t "2015-01" #t "2015-02" nil))))
;; Make sure we don't return nosense results like infinitiy coeficients
;; Fixes https://github.com/metabase/metabase/issues/9070

View File

@@ -248,12 +248,12 @@
:model/Card card-1 {:database_id (mt/id)}
:model/Card card-2 {:database_id (u/the-id db)}]
(with-cards-in-readable-collection! [card-1 card-2]
(is (= true
(card-returned? :database (mt/id) card-1)))
(is (true?
(card-returned? :database (mt/id) card-1)))
(is (= false
(card-returned? :database db card-1)))
(is (= true
(card-returned? :database db card-2))))))
(is (true?
(card-returned? :database db card-2))))))
(deftest ^:parallel authentication-test
(is (= (get request/response-unauthentic :body) (client/client :get 401 "card")))
@@ -271,12 +271,12 @@
:model/Card card-1 {:table_id (u/the-id table-1)}
:model/Card card-2 {:table_id (u/the-id table-2)}]
(with-cards-in-readable-collection! [card-1 card-2]
(is (= true
(card-returned? :table (u/the-id table-1) (u/the-id card-1))))
(is (true?
(card-returned? :table (u/the-id table-1) (u/the-id card-1))))
(is (= false
(card-returned? :table (u/the-id table-2) (u/the-id card-1))))
(is (= true
(card-returned? :table (u/the-id table-2) (u/the-id card-2))))))))
(is (true?
(card-returned? :table (u/the-id table-2) (u/the-id card-2))))))))
;; Make sure `model_id` is required when `f` is :table
(deftest ^:parallel model_id-requied-when-f-is-table
@@ -1419,8 +1419,8 @@
(archived?))]
(is (= false
(archived?)))
(is (= true
(set-archived! true)))
(is (true?
(set-archived! true)))
(is (= false
(set-archived! false)))))))
@@ -2733,8 +2733,8 @@
(mt/with-temporary-setting-values [enable-public-sharing true]
(mt/with-temp [:model/Card card]
(let [{uuid :uuid} (mt/user-http-request :crowberto :post 200 (format "card/%d/public_link" (u/the-id card)))]
(is (= true
(boolean (t2/exists? :model/Card :id (u/the-id card), :public_uuid uuid)))))))))
(is (true?
(boolean (t2/exists? :model/Card :id (u/the-id card), :public_uuid uuid)))))))))
(deftest share-card-preconditions-test
(testing "POST /api/card/:id/public_link"

View File

@@ -1386,11 +1386,11 @@
;; Block waiting for the promises from sync and analyze to be delivered. Should be delivered instantly,
;; however if something went wrong, don't hang forever, eventually timeout and fail
(testing "sync called?"
(is (= true
(deref sync-called? long-timeout :sync-never-called))))
(is (true?
(deref sync-called? long-timeout :sync-never-called))))
(testing "analyze called?"
(is (= true
(deref analyze-called? long-timeout :analyze-never-called))))
(is (true?
(deref analyze-called? long-timeout :analyze-never-called))))
(testing "audit log entry generated"
(is (= db-id
(:model_id (mt/latest-audit-log-entry "database-manual-sync")))))))))))

View File

@@ -237,8 +237,8 @@
:model/Field {field-id :id} {:semantic_type :type/FK :fk_target_field_id fk-field-id}]
(let [original-val (boolean (t2/select-one-fn :fk_target_field_id :model/Field, :id field-id))]
(testing "before API call"
(is (= true
original-val)))
(is (true?
original-val)))
;; unset the :type/FK semantic-type
(mt/user-http-request :crowberto :put 200 (format "field/%d" field-id) {:semantic_type :type/Name})
(testing "after API call"

View File

@@ -123,7 +123,7 @@
(is (= 3.14 (fetch-setting :test-api-setting-double 200)))
(test-api-setting-boolean! true)
(is (= true (fetch-setting :test-api-setting-boolean 200)))
(is (true? (fetch-setting :test-api-setting-boolean 200)))
(test-api-setting-integer! 42)
(is (= 42 (fetch-setting :test-api-setting-integer 200))))))

View File

@@ -998,7 +998,7 @@
(deftest update-groups-test-4
(testing "PUT /api/user/:id"
(testing (str "We should be able to put someone in the Admin group when we update them them (is_superuser = TRUE "
(testing (str "We should be able to put someone in the Admin group when we update them them (is_superuser TRUE? "
"and user_group_memberships including admin group ID)")
(mt/with-premium-features #{}
(mt/with-temp [:model/User {:keys [email id]}]
@@ -1129,8 +1129,8 @@
{:first_name (:first_name user)
:last_name "whatever"
:email (:email user)})
(is (= true
(t2/select-one-fn :is_active :model/User :id (:id user)))
(is (true?
(t2/select-one-fn :is_active :model/User :id (:id user)))
"the user should now be active")))
(testing "error conditions"
@@ -1172,11 +1172,11 @@
(testing "PUT /api/user/:id/password"
(testing "Test that we can reset our own password. If user is a"
(testing "superuser"
(is (= true
(user-can-reset-password? :superuser))))
(is (true?
(user-can-reset-password? :superuser))))
(testing "non-superuser"
(is (= true
(user-can-reset-password? (not :superuser))))))))
(is (true?
(user-can-reset-password? (not :superuser))))))))
(deftest reset-password-permissions-test
(testing "PUT /api/user/:id/password"

View File

@@ -103,3 +103,23 @@
(testing "error message contains allowed models"
(is (= (set (map name (keys @#'api.embed.common/api-name->model)))
(set (:allowed-models (mt/user-http-request :crowberto :post 400 "util/entity_id" {:entity_ids {"Card" [card-eid]}}))))))))
(deftest ^:parallel openapi-test
(testing "GET /api/util/openapi"
(testing "Requires superuser"
(is (= "You don't have permissions to do that."
(mt/user-http-request :rasta :get 403 "util/openapi"))))
(testing "Call successful for superusers"
(let [response (mt/user-http-request :crowberto :get 200 "util/openapi")]
(is (map? response))
(is (= "3.1.0" (:openapi response)))
(is (= "Metabase API" (get-in response [:info :title])))
(is (map? (:paths response)))
(is (map? (:components response))))
(testing "Response contains key components of API spec"
(let [response (mt/user-http-request :crowberto :get 200 "util/openapi")]
(is (contains? (:paths response) "/api/card"))
(is (contains? (:paths response) "/api/user"))
(is (contains? (:paths response) "/api/dashboard")))))))

View File

@@ -215,9 +215,9 @@
;; When there are more rows than the limit, check to ensure a truncation warning is present
(deftest truncation-warning-when-rows-exceed-max
(is (= true
(let [html-output (html (#'body/render-truncation-warning 10 100))]
(boolean (re-find #"Showing.*10.*of.*100.*rows" html-output))))))
(is (true?
(let [html-output (html (#'body/render-truncation-warning 10 100))]
(boolean (re-find #"Showing.*10.*of.*100.*rows" html-output))))))
(def ^:private test-columns-with-date-semantic-type
(update test-columns 2 merge {:base_type :type/Text

View File

@@ -48,10 +48,10 @@
(testing "Make sure *every* line contains ENGINE ... CHARACTER SET ... COLLATE"
(doseq [line (split-migrations-sqls (liquibase/migrations-sql liquibase))
:when (str/starts-with? line "CREATE TABLE")]
(is (= true
(or
(str/includes? line "ENGINE InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci")
(str/includes? line "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci")))
(is (true?
(or
(str/includes? line "ENGINE InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci")
(str/includes? line "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci")))
(format "%s should include ENGINE ... CHARACTER SET ... COLLATE ..." (pr-str line)))))))))
(defn liquibase-file->included-ids

View File

@@ -468,8 +468,8 @@
(ex-data e)))
(take-while some? (iterate ex-cause e)))]
(testing "should be a card Query error"
(is (= true
(boolean (:card-query-error? exc-data)))))
(is (true?
(boolean (:card-query-error? exc-data)))))
(testing "card-id"
(is (= 1
(:card-id exc-data))))

View File

@@ -67,6 +67,7 @@
(deftest ^:parallel json-unfolding-default-test
(testing "JSON Unfolding database support details behave as they're supposed to"
#_{:clj-kondo/ignore [:equals-true]}
(are [details expected] (= expected
(driver.common/json-unfolding-default {:details details}))
{} true

View File

@@ -220,6 +220,7 @@
(deftest ^:parallel check-action-commands-test
(mt/test-driver :h2
#_{:clj-kondo/ignore [:equals-true]}
(are [query] (= true (#'h2/every-command-allowed-for-actions? (#'h2/classify-query (u/the-id (mt/db)) query)))
"select 1"
"update venues set name = 'bill'"

View File

@@ -41,8 +41,8 @@
(tu.async/wait-for-result started-loading-chan 500)
(is (= ::race-condition-test
(driver/the-initialized-driver ::race-condition-test)))
(is (= true
@finished-loading)))))))
(is (true?
@finished-loading)))))))
;;;; [[driver-multimethods-in-changelog-test]]

View File

@@ -134,8 +134,8 @@
(is (= "Bearer\nabc"
(get headers "Authorization")))
(mock-200-response (mock-conversations-response-body request)))}
(is (= true
(slack/valid-token? "abc"))))
(is (true?
(slack/valid-token? "abc"))))
(testing "invalid token should return false"
(http-fake/with-fake-routes {conversations-endpoint (constantly
(mock-200-response {:ok false

View File

@@ -486,6 +486,7 @@
(deftest ^:parallel js=-metatest
(testing "check js= works correctly (who tests the tests?)"
(testing "should be true"
#_{:clj-kondo/ignore [:equals-true]}
(are [a b] (= true (js= a b))
7 7
0 0

View File

@@ -197,6 +197,7 @@
(deftest ^:parallel can-run-test
(mu/disable-enforcement
#_{:clj-kondo/ignore [:equals-true]}
(are [can-run? card-type query]
(= can-run? (lib.query/can-run query card-type))
true :question (lib.tu/venues-query)
@@ -235,6 +236,7 @@
(deftest ^:parallel can-save-test
(mu/disable-enforcement
#_{:clj-kondo/ignore [:equals-true]}
(are [can-save? card-type query]
(= can-save? (lib.query/can-save query card-type))
true :question (lib.tu/venues-query)
@@ -271,16 +273,16 @@
(deftest ^:parallel can-preview-test
(mu/disable-enforcement
(testing "can-preview"
(is (= true (lib/can-preview (lib.tu/venues-query))))
(is (true? (lib/can-preview (lib.tu/venues-query))))
(testing "with an offset expression"
(let [offset-query (lib/expression (lib.tu/venues-query) "prev_price"
(lib/offset (meta/field-metadata :venues :price) -1))]
(testing "without order-by = false"
(is (= false (lib/can-preview offset-query))))
(testing "with order-by = true"
(is (= true (-> offset-query
(lib/order-by (meta/field-metadata :venues :latitude))
lib/can-preview))))))
(is (true? (-> offset-query
(lib/order-by (meta/field-metadata :venues :latitude))
lib/can-preview))))))
(testing "with an offset expression in an earlier stage"
(let [offset-query (-> (lib.tu/venues-query)
(lib/expression "prev_price" (lib/offset (meta/field-metadata :venues :price) -1))
@@ -294,9 +296,9 @@
(lib/order-by -1 (meta/field-metadata :venues :latitude) :asc)
lib/can-preview))))
(testing "with order-by in that stage = true"
(is (= true (-> offset-query
(lib/order-by 0 (meta/field-metadata :venues :latitude) :asc)
lib/can-preview)))))))))
(is (true? (-> offset-query
(lib/order-by 0 (meta/field-metadata :venues :latitude) :asc)
lib/can-preview)))))))))
(def ^:private query-for-preview
"\"Christmas tree\" query with anything and everything hanging from its branches, for testing [[preview-query]]."

View File

@@ -199,7 +199,7 @@
(testing "freshly added breakout columns"
(is (= false (breakout-exists? before stage-number column))
"are not present before")
(is (= true (breakout-exists? after stage-number column))
(is (true? (breakout-exists? after stage-number column))
"are present after")
(testing "go at the end of the list"
(is (= (count after-breakouts)

View File

@@ -192,17 +192,19 @@
(testing pred
(when positive
(testing positive
(is (true? (pred (column positive))))))
(is (true? (pred (column positive))))))
(when negative
(testing negative
(is (false? (pred (column negative))))))))))
(deftest ^:parallel string?-test
#_{:clj-kondo/ignore [:equals-true]}
(are [exp column] (= exp (lib.types.isa/string? column))
true {:effective-type :type/Text :semantic-type :type/SerializedJSON}
false {:effective-type :type/JSON :semantic-type :type/SerializedJSON}))
(deftest ^:parallel valid-filter-for?-test
#_{:clj-kondo/ignore [:equals-true]}
(are [exp base-lhs eff-lhs base-rhs eff-rhs] (= exp (lib.types.isa/valid-filter-for?
{:base-type base-lhs
:effective-type eff-lhs}

View File

@@ -97,6 +97,7 @@
(io/delete-file f)))))))
(deftest level-enabled?-test
#_{:clj-kondo/ignore [:equals-true]}
(are [set-level check-level expected-value] (= expected-value
(mt/with-log-level [metabase.logger-test set-level]
(logger/level-enabled? 'metabase.logger-test check-level)))

View File

@@ -12,14 +12,14 @@
(mt/with-temp [:model/User {user-id :id} {}
:model/LoginHistory history-1 {:user_id user-id, :device_id device-1}]
(testing "one login to device 1 -- should be the first login with this device"
(is (= true
(#'login-history/first-login-on-this-device? history-1)))
(is (= true
(#'login-history/first-login-ever? history-1))))
(is (true?
(#'login-history/first-login-on-this-device? history-1)))
(is (true?
(#'login-history/first-login-ever? history-1))))
(testing "add a history item for a *different* device -- should be the first login with this device"
(mt/with-temp [:model/LoginHistory _ {:user_id user-id, :device_id device-2}]
(is (= true
(#'login-history/first-login-on-this-device? history-1)))
(is (true?
(#'login-history/first-login-on-this-device? history-1)))
(is (= false
(#'login-history/first-login-ever? history-1)))
(testing "add a second history item for device 1 -- should *not* be the first login with this device"

View File

@@ -1097,8 +1097,8 @@
{:namespace "snippets"})]
(mt/with-temp [model object {:collection_id (u/the-id e)}]
(archive-collection! e)
(is (= true
(t2/select-one-fn :archived model :id (u/the-id object)))))))
(is (true?
(t2/select-one-fn :archived model :id (u/the-id object)))))))
(testing (format "Test that archiving applies to %ss belonging to descendant Collections" (name model))
;; object is in E, a descendant of C; archiving C should cause object to be archived
@@ -1106,8 +1106,8 @@
{:namespace "snippets"})]
(mt/with-temp [model object {:collection_id (u/the-id e)}]
(archive-collection! c)
(is (= true
(t2/select-one-fn :archived model :id (u/the-id object)))))))))
(is (true?
(t2/select-one-fn :archived model :id (u/the-id object)))))))))
(deftest nested-collection-unarchiving-objects-test
(doseq [model [:model/Card :model/Dashboard :model/NativeQuerySnippet :model/Pulse]]
@@ -1456,6 +1456,7 @@
(group->perms [a b c] group))))))
(deftest ^:parallel valid-location-path?-test
#_{:clj-kondo/ignore [:equals-true]}
(are [path expected] (= expected
(#'collection/valid-location-path? path))
nil false

View File

@@ -43,13 +43,13 @@
"artifact-permissions regime will be able to see it if they have permissions for that Collection")
(binding [api/*current-user-permissions-set* (delay #{(perms/collection-read-path collection)})]
(mi/perms-objects-set dashtab :read)
(is (= true (mi/can-read? dashtab)))
(is (true? (mi/can-read? dashtab)))
(is (= false (mi/can-write? dashtab)))))
(testing "Do we have *write* Permissions for a dashtab if we have *write* Permissions for the Collection it's in?"
(binding [api/*current-user-permissions-set* (delay #{(perms/collection-readwrite-path collection)})]
(is (= true (mi/can-read? dashtab)))
(is (= true (mi/can-write? dashtab)))))
(is (true? (mi/can-read? dashtab)))
(is (true? (mi/can-write? dashtab)))))
(testing "A user that can't see the Collection that the Dashboard is in can't read and write the dashtab"
(mt/with-current-user (mt/user->id :lucky)
@@ -58,8 +58,8 @@
(testing "A user that can see the Collection that the Dashboard is in can read and write the dashtab"
(mt/with-current-user (mt/user->id :rasta)
(is (= true (mi/can-read? dashboard)))
(is (= true (mi/can-write? dashboard)))))))
(is (true? (mi/can-read? dashboard)))
(is (true? (mi/can-write? dashboard)))))))
(deftest dependency-test
(testing "Deleting a dashtab should delete the associated dashboardcards"

View File

@@ -201,8 +201,8 @@
(testing (str "Check that if a Dashboard is in a Collection, someone who would not be able to see it under the old "
"artifact-permissions regime will be able to see it if they have permissions for that Collection")
(binding [api/*current-user-permissions-set* (atom #{(perms/collection-read-path collection)})]
(is (= true
(mi/can-read? dash)))))
(is (true?
(mi/can-read? dash)))))
(testing (str "Check that if a Dashboard is in a Collection, someone who would otherwise be able to see it under "
"the old artifact-permissions regime will *NOT* be able to see it if they don't have permissions for "

View File

@@ -59,8 +59,8 @@
(deftest newly-created-card-test
(testing "Newly created Card with param referencing Field"
(testing "in On-Demand DB should get updated FieldValues"
(is (= true
(field-values-were-updated-for-new-card?! {:db {:is_on_demand true}}))))
(is (true?
(field-values-were-updated-for-new-card?! {:db {:is_on_demand true}}))))
(testing "in non-On-Demand DB should *not* get updated FieldValues"
(is (= false

View File

@@ -626,8 +626,8 @@
(:has_more_values (chain-filter categories.name {})))))
(testing "`true` if the limit option is less than the count of values of fieldvalues"
(is (= true
(:has_more_values (chain-filter categories.name {} :limit 1)))))
(is (true?
(:has_more_values (chain-filter categories.name {} :limit 1)))))
(testing "`false` if the limit option is greater the count of values of fieldvalues"
(is (= false
(:has_more_values (chain-filter categories.name {} :limit Integer/MAX_VALUE))))))
@@ -635,8 +635,8 @@
(testing "`true` if the values of a field exceeds our [[field-values/*total-max-length*]] limit"
(with-clean-field-values-for-field! (mt/id :categories :name)
(binding [field-values/*total-max-length* 10]
(is (= true
(:has_more_values (chain-filter categories.name {}))))))))
(is (true?
(:has_more_values (chain-filter categories.name {}))))))))
(testing "with contraints"
(with-clean-field-values-for-field! (mt/id :categories :name)
@@ -645,8 +645,8 @@
(:has_more_values (chain-filter categories.name {venues.price 4})))))
(testing "`true` if the limit option is less than the count of values of fieldvalues"
(is (= true
(:has_more_values (chain-filter categories.name {venues.price 4} :limit 1)))))
(is (true?
(:has_more_values (chain-filter categories.name {venues.price 4} :limit 1)))))
(testing "`false` if the limit option is greater the count of values of fieldvalues"
(is (= false
(:has_more_values (chain-filter categories.name {venues.price 4} :limit Integer/MAX_VALUE))))))
@@ -654,8 +654,8 @@
(with-clean-field-values-for-field! (mt/id :categories :name)
(testing "`true` if the values of a field exceeds our [[field-values/*total-max-length*]] limit"
(binding [field-values/*total-max-length* 10]
(is (= true
(:has_more_values (chain-filter categories.name {venues.price 4})))))))))
(is (true?
(:has_more_values (chain-filter categories.name {venues.price 4})))))))))
(testing "for non-cached fields"
(testing "with contraints"
@@ -665,8 +665,8 @@
(:has_more_values (chain-filter venues.latitude {venues.price 4})))))
(testing "`true` if the limit is less than the number of values the field has"
(is (= true
(:has_more_values (chain-filter venues.latitude {venues.price 4} :limit 1))))))))))
(is (true?
(:has_more_values (chain-filter venues.latitude {venues.price 4} :limit 1))))))))))
;; TODO: make this test parallel, but clj-kondo complains about t2/update! being destructive and no amount of
;; :clj-kondo/ignore convinces it.

View File

@@ -79,8 +79,8 @@
(clear-cache!)
(setting-test/toucan-name! "Reggae Toucan")
(simulate-another-instance-updating-setting! :toucan-name "Bird Can")
(is (= true
(#'setting.cache/cache-out-of-date?)))))
(is (true?
(#'setting.cache/cache-out-of-date?)))))
(deftest restore-cache-if-needed-test
(testing (str "of course, `restore-cache-if-needed!` should use TTL memoization, and the cache should not get "

View File

@@ -183,10 +183,10 @@
(is (= nil
(setting/user-facing-value :test-setting-2))))
(testing "`user-facing-value` should work correctly for calculated Settings (no underlying value)"
(is (= true
(test-setting-calculated-getter)))
(is (= true
(setting/user-facing-value :test-setting-calculated-getter))))
(is (true?
(test-setting-calculated-getter)))
(is (true?
(setting/user-facing-value :test-setting-calculated-getter))))
(testing "`user-facing-value` will initialize pending values"
(mt/discard-setting-changes [:test-setting-custom-init]
@@ -324,8 +324,8 @@
(test-setting-1! "COOL")
(is (= "COOL"
(test-setting-1)))
(is (= true
(setting-exists-in-db? :test-setting-1)))
(is (true?
(setting-exists-in-db? :test-setting-1)))
(test-setting-1! nil)
(is (= nil
(test-setting-1)))
@@ -338,8 +338,8 @@
(test-setting-2! "COOL")
(is (= "COOL"
(test-setting-2)))
(is (= true
(setting-exists-in-db? :test-setting-2)))
(is (true?
(setting-exists-in-db? :test-setting-2)))
(test-setting-2! nil)
(is (= "[Default Value]"
(test-setting-2))
@@ -897,16 +897,16 @@
(testing "Boolean Database-local Settings\n"
(testing "Site-wide value is `true`"
(test-boolean-database-local-setting! true)
(is (= true
(test-boolean-database-local-setting))))
(is (true?
(test-boolean-database-local-setting))))
(testing "Site-wide value is `false`"
(test-boolean-database-local-setting! false)
(is (= false
(test-boolean-database-local-setting)))
(testing "Database-local value is `true`"
(binding [setting/*database-local-values* {:test-boolean-database-local-setting "true"}]
(is (= true
(test-boolean-database-local-setting)))))
(is (true?
(test-boolean-database-local-setting)))))
(testing "Database-local value is explicitly set to `nil` -- fall back to site-wide value"
(binding [setting/*database-local-values* {:test-boolean-database-local-setting nil}]
(is (= false

View File

@@ -213,8 +213,8 @@
(testing (str "when you create a new user with `is_superuser` set to `true`, it should create a "
"PermissionsGroupMembership object")
(mt/with-temp [:model/User user {:is_superuser true}]
(is (= true
(t2/exists? :model/PermissionsGroupMembership :user_id (u/the-id user), :group_id (u/the-id (perms-group/admin))))))))
(is (true?
(t2/exists? :model/PermissionsGroupMembership :user_id (u/the-id user), :group_id (u/the-id (perms-group/admin))))))))
(deftest ldap-sequential-login-attributes-test
(testing "You should be able to create a new LDAP user if some `login_attributes` are vectors (#10291)"
@@ -359,8 +359,8 @@
(user-group-names user)))
(testing "their is_superuser flag should be set to true"
(is (= true
(t2/select-one-fn :is_superuser :model/User :id (u/the-id user)))))))
(is (true?
(t2/select-one-fn :is_superuser :model/User :id (u/the-id user)))))))
(testing "should be able to remove someone from the Admin group"
(mt/with-temp [:model/User user {:is_superuser true}]
@@ -380,8 +380,8 @@
(mt/with-temp [:model/User user {:is_superuser true}]
(u/ignore-exceptions
(user/set-permissions-groups! user #{(perms-group/all-users) Integer/MAX_VALUE}))
(is (= true
(t2/select-one-fn :is_superuser :model/User :id (u/the-id user)))))))
(is (true?
(t2/select-one-fn :is_superuser :model/User :id (u/the-id user)))))))
(testing "Invalid REMOVE operation"
;; Attempt to remove someone from All Users + add to a valid group at the same time -- neither should persist

View File

@@ -286,22 +286,22 @@
goal-met? (requiring-resolve 'metabase.notification.payload.impl.card/goal-met?)]
(testing "Progress bar"
(testing "alert above"
(testing "value below goal" (is (= false (goal-met? alert-above-pulse (progress-result 4)))))
(testing "value equals goal" (is (= true (goal-met? alert-above-pulse (progress-result 5)))))
(testing "value above goal" (is (= true (goal-met? alert-above-pulse (progress-result 6))))))
(testing "value below goal" (is (= false (goal-met? alert-above-pulse (progress-result 4)))))
(testing "value equals goal" (is (true? (goal-met? alert-above-pulse (progress-result 5)))))
(testing "value above goal" (is (true? (goal-met? alert-above-pulse (progress-result 6))))))
(testing "alert below"
(testing "value below goal" (is (= true (goal-met? alert-below-pulse (progress-result 4)))))
(testing "value equals goal (#10899)" (is (= false (goal-met? alert-below-pulse (progress-result 5)))))
(testing "value above goal" (is (= false (goal-met? alert-below-pulse (progress-result 6)))))))
(testing "value below goal" (is (true? (goal-met? alert-below-pulse (progress-result 4)))))
(testing "value equals goal (#10899)" (is (= false (goal-met? alert-below-pulse (progress-result 5)))))
(testing "value above goal" (is (= false (goal-met? alert-below-pulse (progress-result 6)))))))
(testing "Timeseries"
(testing "alert above"
(testing "value below goal" (is (= false (goal-met? alert-above-pulse (timeseries-result 4)))))
(testing "value equals goal" (is (= true (goal-met? alert-above-pulse (timeseries-result 5)))))
(testing "value above goal" (is (= true (goal-met? alert-above-pulse (timeseries-result 6))))))
(testing "value below goal" (is (= false (goal-met? alert-above-pulse (timeseries-result 4)))))
(testing "value equals goal" (is (true? (goal-met? alert-above-pulse (timeseries-result 5)))))
(testing "value above goal" (is (true? (goal-met? alert-above-pulse (timeseries-result 6))))))
(testing "alert below"
(testing "value below goal" (is (= true (goal-met? alert-below-pulse (timeseries-result 4)))))
(testing "value equals goal" (is (= false (goal-met? alert-below-pulse (timeseries-result 5)))))
(testing "value above goal" (is (= false (goal-met? alert-below-pulse (timeseries-result 6)))))))))
(testing "value below goal" (is (true? (goal-met? alert-below-pulse (timeseries-result 4)))))
(testing "value equals goal" (is (= false (goal-met? alert-below-pulse (timeseries-result 5)))))
(testing "value above goal" (is (= false (goal-met? alert-below-pulse (timeseries-result 6)))))))))
(deftest send-condition-above-goal-test
(testing "skip is the goal is not met"

View File

@@ -13,8 +13,8 @@
(testing "when you create a PermissionsGroupMembership for a User in the admin group, it should set their `is_superuser` flag"
(mt/with-temp [:model/User user]
(t2/insert! :model/PermissionsGroupMembership {:user_id (u/the-id user), :group_id (u/the-id (perms-group/admin))})
(is (= true
(t2/select-one-fn :is_superuser :model/User :id (u/the-id user)))))))
(is (true?
(t2/select-one-fn :is_superuser :model/User :id (u/the-id user)))))))
(deftest remove-is-superuser-test
(testing "when you delete a PermissionsGroupMembership for a User in the admin group, it should set their `is_superuser` flag"

View File

@@ -14,8 +14,8 @@
(testing "valid paths"
(doseq [path valid-paths]
(testing (pr-str path)
(is (= true
(perms.u/valid-path? path)))))))
(is (true?
(perms.u/valid-path? path)))))))
(deftest ^:parallel valid-path-test-2
(testing "invalid paths"

View File

@@ -81,8 +81,8 @@
(testing "Changing `site-url` to non-HTTPS should disable forced HTTPS redirection"
(mt/with-temporary-setting-values [site-url "https://example.com"
redirect-all-requests-to-https true]
(is (= true
(public-settings/redirect-all-requests-to-https)))
(is (true?
(public-settings/redirect-all-requests-to-https)))
(public-settings/site-url! "http://example.com")
(is (= false
(public-settings/redirect-all-requests-to-https)))))
@@ -90,11 +90,11 @@
(testing "Changing `site-url` to non-HTTPS should disable forced HTTPS redirection"
(mt/with-temporary-setting-values [site-url "https://example.com"
redirect-all-requests-to-https true]
(is (= true
(public-settings/redirect-all-requests-to-https)))
(is (true?
(public-settings/redirect-all-requests-to-https)))
(public-settings/site-url! "https://different.example.com")
(is (= true
(public-settings/redirect-all-requests-to-https))))))
(is (true?
(public-settings/redirect-all-requests-to-https))))))
(deftest translate-public-setting
(mt/with-mock-i18n-bundles! {"zz" {:messages {"Host" "HOST"}}}
@@ -106,9 +106,9 @@
(deftest tru-translates
(mt/with-mock-i18n-bundles! {"zz" {:messages {"Host" "HOST"}}}
(mt/with-user-locale "zz"
(is (= true
(= (i18n/locale "zz")
(i18n/user-locale))))
(is (true?
(= (i18n/locale "zz")
(i18n/user-locale))))
(is (= "HOST"
(tru "Host"))))))
@@ -173,8 +173,8 @@
(mt/with-temporary-setting-values [site-url "https://example.com"
redirect-all-requests-to-https false]
(public-settings/redirect-all-requests-to-https! v)
(is (= true
(public-settings/redirect-all-requests-to-https)))))
(is (true?
(public-settings/redirect-all-requests-to-https)))))
(testing "\n`site-url` is not HTTPS"
(mt/with-temporary-setting-values [site-url "http://example.com"
@@ -359,7 +359,7 @@
(mt/with-premium-features #{:whitelabel}
(testing "When whitelabeling is enabled, show-metabase-links setting can be set to boolean"
(public-settings/show-metabase-links! true)
(is (= true (public-settings/show-metabase-links)))
(is (true? (public-settings/show-metabase-links)))
(public-settings/show-metabase-links! false)
(is (= false (public-settings/show-metabase-links)))))
@@ -371,7 +371,7 @@
#"Setting show-metabase-links is not enabled because feature :whitelabel is not available"
(public-settings/show-metabase-links! true)))
(is (= true (public-settings/show-metabase-links)))))))
(is (true? (public-settings/show-metabase-links)))))))
(def prevent? #'public-settings/prevent-upgrade?)

View File

@@ -564,8 +564,8 @@
(perms/grant-collection-readwrite-permissions! (perms-group/all-users) collection)
(mt/user-http-request :rasta :put 200 (str "pulse/" (u/the-id pulse))
{:archived true})
(is (= true
(t2/select-one-fn :archived :model/Pulse :id (u/the-id pulse)))))))
(is (true?
(t2/select-one-fn :archived :model/Pulse :id (u/the-id pulse)))))))
(deftest unarchive-test
(testing "Can we unarchive a Pulse?"

View File

@@ -332,14 +332,14 @@
:assert
{:email
(fn [_ [email]]
(is (= true (some? email))
(is (true? (some? email))
"Should have a message in the inbox")
(when email
(let [filename (-> email :message last :content)
exists? (some-> filename io/file .exists)]
(testing "File should exist"
(is (= true
exists?)))
(is (true?
exists?)))
(testing (str "tmp file = %s" filename)
(testing "Slurp in the generated CSV and count the lines found in the file"
(when exists?
@@ -558,22 +558,22 @@
goal-met? (requiring-resolve 'metabase.notification.payload.impl.card/goal-met?)]
(testing "Progress bar"
(testing "alert above"
(testing "value below goal" (is (= false (goal-met? alert-above-pulse (progress-result 4)))))
(testing "value equals goal" (is (= true (goal-met? alert-above-pulse (progress-result 5)))))
(testing "value above goal" (is (= true (goal-met? alert-above-pulse (progress-result 6))))))
(testing "value below goal" (is (false? (goal-met? alert-above-pulse (progress-result 4)))))
(testing "value equals goal" (is (true? (goal-met? alert-above-pulse (progress-result 5)))))
(testing "value above goal" (is (true? (goal-met? alert-above-pulse (progress-result 6))))))
(testing "alert below"
(testing "value below goal" (is (= true (goal-met? alert-below-pulse (progress-result 4)))))
(testing "value equals goal (#10899)" (is (= false (goal-met? alert-below-pulse (progress-result 5)))))
(testing "value above goal" (is (= false (goal-met? alert-below-pulse (progress-result 6)))))))
(testing "value below goal" (is (true? (goal-met? alert-below-pulse (progress-result 4)))))
(testing "value equals goal (#10899)" (is (false? (goal-met? alert-below-pulse (progress-result 5)))))
(testing "value above goal" (is (false? (goal-met? alert-below-pulse (progress-result 6)))))))
(testing "Timeseries"
(testing "alert above"
(testing "value below goal" (is (= false (goal-met? alert-above-pulse (timeseries-result 4)))))
(testing "value equals goal" (is (= true (goal-met? alert-above-pulse (timeseries-result 5)))))
(testing "value above goal" (is (= true (goal-met? alert-above-pulse (timeseries-result 6))))))
(testing "value below goal" (is (false? (goal-met? alert-above-pulse (timeseries-result 4)))))
(testing "value equals goal" (is (true? (goal-met? alert-above-pulse (timeseries-result 5)))))
(testing "value above goal" (is (true? (goal-met? alert-above-pulse (timeseries-result 6))))))
(testing "alert below"
(testing "value below goal" (is (= true (goal-met? alert-below-pulse (timeseries-result 4)))))
(testing "value equals goal" (is (= false (goal-met? alert-below-pulse (timeseries-result 5)))))
(testing "value above goal" (is (= false (goal-met? alert-below-pulse (timeseries-result 6)))))))))
(testing "value below goal" (is (true? (goal-met? alert-below-pulse (timeseries-result 4)))))
(testing "value equals goal" (is (false? (goal-met? alert-below-pulse (timeseries-result 5)))))
(testing "value above goal" (is (false? (goal-met? alert-below-pulse (timeseries-result 6)))))))))
(deftest native-query-with-user-specified-axes-test
(testing "Native query with user-specified x and y axis"

View File

@@ -183,8 +183,8 @@
(deftest return-cached-results-test
(testing "if we run the query twice, the second run should return cached results"
(with-mock-cache! [save-chan]
(is (= true
(cacheable?)))
(is (true?
(cacheable?)))
(run-query)
(mt/wait-for-result save-chan)
(is (= :cached
@@ -248,7 +248,7 @@
(deftest ignore-cached-results-should-still-save-test
(testing "...but if it's set those results should still be cached for next time."
(with-mock-cache! [save-chan]
(is (= true (cacheable?)))
(is (true? (cacheable?)))
(run-query :middleware {:ignore-cached-results? true})
(mt/wait-for-result save-chan)
(is (= :cached (run-query))))))
@@ -278,16 +278,16 @@
(mt/wait-for-result save-chan)
(let [query-hash (qp.util/query-hash (test-query nil))]
(testing "Cached results should exist"
(is (= true
(i/cached-results cache/*backend* query-hash (ttl-strategy)
some?))))
(is (true?
(i/cached-results cache/*backend* query-hash (ttl-strategy)
some?))))
(i/save-results! cache/*backend* query-hash (byte-array [0 0 0]))
(testing "Invalid cache entry should be handled gracefully"
(is (= :not-cached
(run-query))))))))
(deftest metadata-test
(testing "Verify that correct metadata about caching such as `:updated_at` and `:cached` come back with cached results."
(testing "Verify that correct metadata about caching such as `:updated_at` and `:cached` come back with cached results"
(with-mock-cache! [save-chan]
(mt/with-clock #t "2020-02-19T02:31:07.798Z[UTC]"
(run-query)
@@ -330,8 +330,8 @@
(with-mock-cache! [save-chan]
(let [query (assoc query :cache-strategy (ttl-strategy))]
(testing (format "query = %s" (pr-str query))
(is (= true
(boolean (#'cache/is-cacheable? query)))
(is (true?
(boolean (#'cache/is-cacheable? query)))
"Query should be cacheable")
(mt/with-clock #t "2020-02-19T04:44:26.056Z[UTC]"
@@ -592,8 +592,8 @@
(fn [rff]
(qp/process-query query rff)))
(mt/wait-for-result save-chan))
(is (= true
(:cached (:cache/details (qp/process-query query))))
(is (true?
(:cached (:cache/details (qp/process-query query))))
"Results should be cached")
(let [uncached-results (with-open [ostream (java.io.PipedOutputStream.)
istream (java.io.PipedInputStream. ostream)

View File

@@ -171,8 +171,8 @@
:async-context (reify AsyncContext
(complete [_]
(deliver complete-promise true)))})
(is (= true
(deref complete-promise 1000 ::timed-out)))
(is (true?
(deref complete-promise 1000 ::timed-out)))
(let [response-str (String. (.toByteArray os) "UTF-8")]
(is (= "[{\"num_cans\":\"2\"}]"
(str/replace response-str #"\n+" "")))

View File

@@ -9,6 +9,7 @@
(set! *warn-on-reflection* true)
(deftest ^:parallel query-without-aggregations-or-limits?-test
#_{:clj-kondo/ignore [:equals-true]}
(are [x expected] (= expected
(qp.util/query-without-aggregations-or-limits? x))
{:query {:aggregation [[:count]]}} false

View File

@@ -483,30 +483,30 @@
;; Allow a 30 second window for the current time to account for any difference between the time in Clojure and the DB
(doseq [timezone [nil "America/Los_Angeles"]]
(mt/with-report-timezone-id! timezone
(is (= true
(-> (mt/run-mbql-query venues
{:expressions {"1" [:now]}
:fields [[:expression "1"]]
:limit 1})
mt/rows
ffirst
u.date/parse
(t/zoned-date-time (t/zone-id "UTC")) ; needed for sqlite, which returns a local date time
(close? (t/instant) (t/seconds 30))))))))))
(is (true?
(-> (mt/run-mbql-query venues
{:expressions {"1" [:now]}
:fields [[:expression "1"]]
:limit 1})
mt/rows
ffirst
u.date/parse
(t/zoned-date-time (t/zone-id "UTC")) ; needed for sqlite, which returns a local date time
(close? (t/instant) (t/seconds 30))))))))))
(deftest ^:parallel now-test-2
(mt/test-drivers (mt/normal-drivers-with-feature :now :date-arithmetics)
(testing "should work as an argument to datetime-add and datetime-subtract"
(is (= true
(-> (mt/run-mbql-query venues
{:expressions {"1" [:datetime-subtract [:datetime-add [:now] 1 :day] 1 :day]}
:fields [[:expression "1"]]
:limit 1})
mt/rows
ffirst
u.date/parse
(t/zoned-date-time (t/zone-id "UTC"))
(close? (t/instant) (t/seconds 30))))))))
(is (true?
(-> (mt/run-mbql-query venues
{:expressions {"1" [:datetime-subtract [:datetime-add [:now] 1 :day] 1 :day]}
:fields [[:expression "1"]]
:limit 1})
mt/rows
ffirst
u.date/parse
(t/zoned-date-time (t/zone-id "UTC"))
(close? (t/instant) (t/seconds 30))))))))
(deftest ^:parallel now-test-3
(mt/test-drivers (mt/normal-drivers-with-feature :now)

View File

@@ -805,8 +805,8 @@
"Card 1" card-1
"Card 2" card-2}]
(testing object-name
(is (= true
(mi/can-read? object)))))
(is (true?
(mi/can-read? object)))))
(testing "\nshould be able to run the query"
(is (= [[1 "Red Medicine" 4 10.0646 -165.374 3]

View File

@@ -103,6 +103,7 @@
{:search-native-query true})))))))
(deftest joined-with-table?-test
#_{:clj-kondo/ignore [:equals-true]}
(are [expected args]
(= expected (apply #'search.filter/joined-with-table? args))

View File

@@ -164,8 +164,8 @@
(mt/with-temp [:model/Segment {:keys [id]}]
(is (map? (mt/user-http-request :crowberto :put 200 (str "segment/" id)
{:archived true, :revision_message "Archive the Segment"})))
(is (= true
(t2/select-one-fn :archived :model/Segment :id id)))))))
(is (true?
(t2/select-one-fn :archived :model/Segment :id id)))))))
(deftest unarchive-test
(testing "PUT /api/segment/:id"

View File

@@ -231,8 +231,8 @@
;; a trick to run this test in OSS because even if advanced-permisison is enabled but EE ns is not evailable
;; `enable-advanced-permissions?` will still return false
(with-redefs [premium-features/enable-advanced-permissions? (fn [& _args] true)]
(is (= true
(:is-group-manager? (#'mw.session/current-user-info-for-session test-session-key nil)))))))
(is (true?
(:is-group-manager? (#'mw.session/current-user-info-for-session test-session-key nil)))))))
(finally
(t2/delete! :model/Session :id test-session-id)))))

View File

@@ -171,7 +171,7 @@
:async-context (reify AsyncContext
(complete [_]
(deliver complete-promise true)))})
(is (= true
(deref complete-promise 1000 ::timed-out)))
(is (true?
(deref complete-promise 1000 ::timed-out)))
(is (= "2 cans"
(String. (.toByteArray os) "UTF-8")))))))

View File

@@ -241,8 +241,8 @@
(mt/user-http-request :rasta :post 204 "session/forgot_password"
{:email (:username (mt/user->credentials :rasta))}))
"Request should return no content")
(is (= true
(reset-fields-set?))
(is (true?
(reset-fields-set?))
"User `:reset_token` and `:reset_triggered` should be updated")
(is (mt/received-email-subject? :rasta #"Password Reset")))))
(testing "We use `site-url` in the email"

View File

@@ -36,8 +36,8 @@
(t2/with-call-count [call-count]
;; call has-user-setup several times.
(dotimes [_ 5]
(is (= true
(setup/has-user-setup))))
(is (true?
(setup/has-user-setup))))
;; `has-user-setup` should have done at most one application database call, as opposed to one call per call to
;; the getter
(is (contains? #{0 1} (call-count)))))
@@ -55,8 +55,8 @@
10)))))) ;; in dev/test we check settings for an override
(testing "Switch back to the 'normal' app DB; value should still be cached for it"
(t2/with-call-count [call-count]
(is (= true
(setup/has-user-setup)))
(is (true?
(setup/has-user-setup)))
(is (zero? (call-count))))))
(deftest has-example-dashboard-id-setting-test

View File

@@ -65,9 +65,9 @@
(with-redefs [ldap/test-ldap-connection (constantly {:status :SUCCESS})]
(testing "LDAP port is saved as default value if passed as an empty string (#18936)"
(is (= true
(mt/user-http-request :crowberto :put 200 "ldap/settings"
(assoc (ldap-test-details) :ldap-port ""))))
(is (true?
(mt/user-http-request :crowberto :put 200 "ldap/settings"
(assoc (ldap-test-details) :ldap-port ""))))
(is (= 389 (sso.settings/ldap-port)))))
(testing "Could update with obfuscated password"

View File

@@ -43,6 +43,7 @@
(deftest allow-autocreation-test
(with-no-sso-google-token!
(mt/with-temporary-setting-values [google-auth-auto-create-accounts-domain "metabase.com"]
#_{:clj-kondo/ignore [:equals-true]}
(are [allowed? email] (= allowed?
(#'google/autocreate-user-allowed-for-email? email))
true "cam@metabase.com"

View File

@@ -44,8 +44,8 @@
(.close ^LDAPConnectionPool (#'ldap/get-connection)))))
(testing "login should succeed"
(is (= true
(ldap/verify-password "cn=Directory Manager" "password"))))
(is (true?
(ldap/verify-password "cn=Directory Manager" "password"))))
(testing "wrong password"
(is (= false
@@ -56,16 +56,16 @@
(ldap/verify-password "cn=Nobody,ou=nowhere,dc=metabase,dc=com" "password"))))
(testing "regular user login"
(is (= true
(ldap/verify-password "cn=Sally Brown,ou=People,dc=metabase,dc=com" "1234"))))
(is (true?
(ldap/verify-password "cn=Sally Brown,ou=People,dc=metabase,dc=com" "1234"))))
(testing "fail regular user login with bad password"
(is (= false
(ldap/verify-password "cn=Sally Brown,ou=People,dc=metabase,dc=com" "password"))))
(testing "password containing dollar signs succeeds (#15145)"
(is (= true
(ldap/verify-password "cn=Fred Taylor,ou=People,dc=metabase,dc=com", "pa$$word"))))))
(is (true?
(ldap/verify-password "cn=Fred Taylor,ou=People,dc=metabase,dc=com", "pa$$word"))))))
(deftest find-test
;; there are EE-specific versions of this test in [[metabase-enterprise.enhancements.integrations.ldap-test]]

View File

@@ -237,8 +237,8 @@
:model/Field field (fake-field table)]
(set-table-visibility-type-via-api! table "hidden")
(set-table-visibility-type-via-api! table nil)
(is (= true
(fake-field-was-analyzed? field)))))))
(is (true?
(fake-field-was-analyzed? field)))))))
(deftest dont-analyze-rehidden-table-test
(testing "re-hiding a table should not cause it to be analyzed"

View File

@@ -4,6 +4,7 @@
[metabase.sync.sync-metadata.fields.common :as common]))
(deftest canonical-names-equal?-test
#_{:clj-kondo/ignore [:equals-true]}
(are [s1 s2 result] (= result (#'common/canonical-names-equal? {:name s1} {:name s2}))
"id" "ID" true
"Id" "id" true

View File

@@ -118,8 +118,8 @@
[results] (:operation-results
(call-with-operation-info! #(sync-util/run-sync-operation process-name mock-db sync-steps)))]
(testing "valid operation metadata?"
(is (= true
(validate-times results))))
(is (true?
(validate-times results))))
(testing "valid step metadata?"
(is (= [true true]
(map (comp validate-times second) (:steps results)))))
@@ -186,43 +186,43 @@
(create-test-sync-summary step-name
(constantly step-log-text)))]
(testing "has-operation?"
(is (= true
(str/includes? results operation))))
(is (true?
(str/includes? results operation))))
(testing "has-db-name?"
(is (= true
(str/includes? results db-name))))
(is (true?
(str/includes? results db-name))))
(testing "has-operation-duration?"
(is (= true
(str/includes? results "5.0 s"))))
(is (true?
(str/includes? results "5.0 s"))))
(testing "has-step-name?"
(is (= true
(str/includes? results step-name))))
(is (true?
(str/includes? results step-name))))
(testing "has-step-duration?"
(is (= true
(str/includes? results "4.0 s"))))
(is (true?
(str/includes? results "4.0 s"))))
(testing "has-log-summary-text?"
(is (= true
(str/includes? results step-log-text))))))
(is (true?
(str/includes? results step-log-text))))))
(testing (str "The `log-summary-fn` part of step info is optional as not all steps have it. Validate that we"
" properly handle that case")
(let [results (#'sync-util/make-log-sync-summary-str operation
(mi/instance :model/Database {:name db-name})
(create-test-sync-summary step-name nil))]
(testing "has-operation?"
(is (= true
(str/includes? results operation))))
(is (true?
(str/includes? results operation))))
(testing "has-db-name?"
(is (= true
(str/includes? results db-name))))
(is (true?
(str/includes? results db-name))))
(testing "has-operation-duration?"
(is (= true
(str/includes? results "5.0 s"))))
(is (true?
(str/includes? results "5.0 s"))))
(testing "has-step-name?"
(is (= true
(str/includes? results step-name))))
(is (true?
(str/includes? results step-name))))
(testing "has-step-duration?"
(is (= true
(str/includes? results "4.0 s"))))))))
(is (true?
(str/includes? results "4.0 s"))))))))
(derive ::sync-error-handling-begin ::sync-util/event)
(derive ::sync-error-handling-end ::sync-util/event)

View File

@@ -39,8 +39,7 @@
(coercion-hierarchies/define-types! ::Coerce-Int-To-Str :type/Integer :type/Text)
(deftest ^:parallel is-coercible?-test
(is (= true
(types/is-coercible? ::Coerce-Int-To-Str :type/Integer :type/Text)))
(is (true? (types/is-coercible? ::Coerce-Int-To-Str :type/Integer :type/Text)))
(testing "should be able to coerce from a subtype of base type"
(is (types/is-coercible? ::Coerce-Int-To-Str :type/BigInteger :type/Text)))
(testing "should NOT be able to coerce from a parent type of base type"

View File

@@ -63,7 +63,7 @@
(mt/with-model-cleanup [:model/UserKeyValue]
(testing "Deletion works"
(mt/user-http-request :rasta :put 200 "/user-key-value/namespace/other/key/other" {:value "true"})
(is (= true (mt/user-http-request :rasta :get 200 "/user-key-value/namespace/other/key/other")))
(is (true? (mt/user-http-request :rasta :get 200 "/user-key-value/namespace/other/key/other")))
(mt/user-http-request :rasta :delete 200 "/user-key-value/namespace/other/key/other")
(is (= nil (mt/user-http-request :rasta :get 204 "/user-key-value/namespace/other/key/other"))))))

View File

@@ -497,8 +497,8 @@
(doseq [t ((juxt t/instant t/local-date t/local-date-time t/offset-date-time identity)
(t/zoned-date-time "2019-11-01T00:00-08:00[US/Pacific]"))]
(testing (format "t = %s" (pr-str t))
(is (= true
(u.date/older-than? t (t/weeks 2)))
(is (true?
(u.date/older-than? t (t/weeks 2)))
(format "%s happened before 2019-11-19" (pr-str t)))
(is (= false
(u.date/older-than? t (t/months 2)))

View File

@@ -230,6 +230,7 @@
(h2x/with-database-type-info (h2x/with-database-type-info :field "date") nil))))))
(deftest ^:parallel is-of-type?-test
#_{:clj-kondo/ignore [:equals-true]}
(are [expr tyype expected] (= expected (h2x/is-of-type? expr tyype))
typed-form "text" true
typed-form "TEXT" true

View File

@@ -139,8 +139,8 @@
(f 2)))))))))
(deftest ^:parallel localized-string?-test
(is (= true
(i18n/localized-string? (i18n/deferred-trs "WOW"))))
(is (true?
(i18n/localized-string? (i18n/deferred-trs "WOW"))))
(is (= false
(i18n/localized-string? "WOW"))))

View File

@@ -8,6 +8,7 @@
(deftest ^:parallel host-up?-test
(testing "host-up?"
#_{:clj-kondo/ignore [:equals-true]}
(are [s expected] (= expected
(u/host-up? s))
"localhost" true
@@ -17,6 +18,7 @@
(u/host-port-up? "nosuchhost" 8005)))))
(deftest ^:parallel ip-address?-test
#_{:clj-kondo/ignore [:equals-true]}
(are [x expected] (= expected
(u/ip-address? x))
"8.8.8.8" true

View File

@@ -12,6 +12,7 @@
(deftest bigint?-test
(testing "should check if the value is a bigint"
#_{:clj-kondo/ignore [:equals-true]}
(are [exp value] (= exp (u.number/bigint? value))
true (u.number/bigint 10)
true (u.number/bigint "9007199254740993")
@@ -20,6 +21,7 @@
(deftest integer?-test
(testing "should check if the value is an integer"
#_{:clj-kondo/ignore [:equals-true]}
(are [exp value] (= exp (u.number/integer? value))
true 0
true 10

View File

@@ -26,6 +26,7 @@
(u/add-period " "))))
(deftest ^:parallel url?-test
#_{:clj-kondo/ignore [:equals-true]}
(are [s expected] (= expected
(u/url? s))
"http://google.com" true
@@ -61,6 +62,7 @@
"http:/" false))
(deftest ^:parallel state?-test
#_{:clj-kondo/ignore [:equals-true]}
(are [x expected] (= expected
(u/state? x))
"louisiana" true
@@ -159,6 +161,7 @@
{} [:c] {}))
(deftest ^:parallel base64-string?-test
#_{:clj-kondo/ignore [:equals-true]}
(are [s expected] (= expected
(u/base64-string? s))
"ABc=" true
@@ -296,6 +299,7 @@
"metabase.com" "cam.saul+1@metabase.com"))
(deftest ^:parallel email-in-domain-test
#_{:clj-kondo/ignore [:equals-true]}
(are [in-domain? email domain] (= in-domain?
(u/email-in-domain? email domain))
true "cam@metabase.com" "metabase.com"
@@ -440,6 +444,7 @@
:to-compare #(dissoc % :id :god_id)})))))
(deftest ^:parallel empty-or-distinct?-test
#_{:clj-kondo/ignore [:equals-true]}
(are [xs expected] (= expected
(u/empty-or-distinct? xs))
nil true

View File

@@ -552,7 +552,7 @@
(is (= source-database-id query-db-id))
(is (= source-table-id magic-card-table-id))
(is (= (format "card__%s" source-card-id) source-table))
(is (= true (every? (fn [[_ id]] (valid-source-ids id)) breakout)))))
(is (true? (every? (fn [[_ id]] (valid-source-ids id)) breakout)))))
(defn- ensure-dashboard-sourcing [source-card dashboard]
(doseq [magic-card (->> dashboard