Compare commits
50 Commits
rolodato/d
...
claude-cod
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aae19be618 | ||
|
|
407dd14106 | ||
|
|
3a8184b185 | ||
|
|
8d8a3e2a96 | ||
|
|
7821b79043 | ||
|
|
d28ff2f121 | ||
|
|
d02f02ee0b | ||
|
|
e93e4e05c0 | ||
|
|
2eb994bd95 | ||
|
|
e613f283db | ||
|
|
4d888a1de4 | ||
|
|
f1f5dde5d8 | ||
|
|
160bbec78d | ||
|
|
d934b0babf | ||
|
|
8686c53446 | ||
|
|
72001629e6 | ||
|
|
5c845ba3c3 | ||
|
|
3d42a5c78c | ||
|
|
a638e855da | ||
|
|
a1b8c3b5b8 | ||
|
|
e67b2d64fc | ||
|
|
e105a57db1 | ||
|
|
ba1500863a | ||
|
|
a51ab2900c | ||
|
|
e934b54f41 | ||
|
|
328ebbcead | ||
|
|
a5f4c4a190 | ||
|
|
3848e58b39 | ||
|
|
b8345f0176 | ||
|
|
03f140aedc | ||
|
|
aabf32d9f2 | ||
|
|
4c129b2d75 | ||
|
|
6bd5115ca3 | ||
|
|
d8dcb4c67e | ||
|
|
e1f803afd1 | ||
|
|
1897a37ade | ||
|
|
472c718b70 | ||
|
|
cfbb5b1197 | ||
|
|
9b88ab8ed3 | ||
|
|
d73c1f0581 | ||
|
|
d423ca80a0 | ||
|
|
ac78a9f92d | ||
|
|
556daad590 | ||
|
|
26053e133b | ||
|
|
4464be1a8b | ||
|
|
624c118990 | ||
|
|
b0d4aea79d | ||
|
|
7ff41f59eb | ||
|
|
b645d99b84 | ||
|
|
2b6bc390b3 |
@@ -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
126
CLAUDE.md
@@ -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
51
bb.edn
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -65,8 +65,7 @@
|
||||
(defn privatize-symbol! [[symb {:keys [analysis]}]]
|
||||
(lsp-cmd/call-command
|
||||
:cycle-privacy
|
||||
{:loc "?"
|
||||
:db}))
|
||||
{:loc "?" :db "?"}))
|
||||
|
||||
(comment
|
||||
|
||||
|
||||
@@ -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?*)))))
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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))))))))
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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))))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
121
mage/mage/be_dev.clj
Normal 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))
|
||||
@@ -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
|
||||
|
||||
|
||||
183
mage/mage/readability_check.clj
Normal file
183
mage/mage/readability_check.clj
Normal 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}}))
|
||||
@@ -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)))))))
|
||||
|
||||
|
||||
@@ -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)))))))))
|
||||
|
||||
@@ -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"}]})})
|
||||
|
||||
@@ -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)))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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")))))))))))
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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))))))
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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")))))))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'"
|
||||
|
||||
@@ -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]]
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]]."
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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)))
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 "
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 "
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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?)
|
||||
|
||||
|
||||
@@ -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?"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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+" "")))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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))
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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)))))
|
||||
|
||||
|
||||
@@ -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")))))))
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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]]
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"))))))
|
||||
|
||||
|
||||
@@ -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)))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"))))
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user