From 8845188509d2954e753ba855e4b5ee203889be9f Mon Sep 17 00:00:00 2001 From: Cam Saul <1455846+camsaul@users.noreply.github.com> Date: Thu, 10 Aug 2023 18:45:01 -0700 Subject: [PATCH] Test metadata helper function consolidation (#33031) * Test metadata helper function consolidation * More code consolidation --- src/metabase/lib/core.cljc | 3 +- src/metabase/lib/query.cljc | 19 +-- test/metabase/lib/card_test.cljc | 23 ++-- test/metabase/lib/column_group_test.cljc | 20 ++-- test/metabase/lib/field_test.cljc | 30 +++-- test/metabase/lib/filter_test.cljc | 6 +- test/metabase/lib/join_test.cljc | 18 +-- test/metabase/lib/metadata/jvm_test.clj | 22 ++-- test/metabase/lib/metadata_test.cljc | 7 +- test/metabase/lib/native_test.cljc | 17 ++- test/metabase/lib/query_test.cljc | 14 +-- test/metabase/lib/test_metadata.cljc | 141 ++++------------------- test/metabase/lib/test_util.cljc | 135 ++++++++++++---------- 13 files changed, 174 insertions(+), 281 deletions(-) diff --git a/src/metabase/lib/core.cljc b/src/metabase/lib/core.cljc index ffba3a97dd9..9d3af8f64c3 100644 --- a/src/metabase/lib/core.cljc +++ b/src/metabase/lib/core.cljc @@ -224,8 +224,7 @@ [lib.normalize normalize] [lib.query - query - saved-question-query] + query] [lib.ref ref] [lib.remove-replace diff --git a/src/metabase/lib/query.cljc b/src/metabase/lib/query.cljc index 366cb337e70..38b5499776a 100644 --- a/src/metabase/lib/query.cljc +++ b/src/metabase/lib/query.cljc @@ -10,8 +10,6 @@ [metabase.lib.schema :as lib.schema] [metabase.lib.schema.id :as lib.schema.id] [metabase.lib.util :as lib.util] - [metabase.shared.util.i18n :as i18n] - [metabase.util.log :as log] [metabase.util.malli :as mu])) (defmethod lib.normalize/normalize :mbql/query @@ -62,7 +60,7 @@ (mu/defn ^:private query-from-existing :- ::lib.schema/query [metadata-providerable :- lib.metadata/MetadataProviderable query :- lib.util/LegacyOrPMBQLQuery] - (let [query (lib.util/pipeline query)] + (let [query (lib.convert/->pMBQL query)] (query-with-stages metadata-providerable (:stages query)))) (defmulti ^:private ->query @@ -96,21 +94,6 @@ x] (->query metadata-providerable x)) -(mu/defn saved-question-query :- ::lib.schema/query - "Convenience for creating a query from a Saved Question (i.e., a Card)." - [metadata-providerable :- lib.metadata/MetadataProviderable - {mbql-query :dataset-query, metadata :result-metadata, :as saved-question}] - (assert mbql-query (i18n/tru "Saved Question is missing query")) - (when-not metadata - (log/warn (i18n/trs "Saved Question {0} {1} is missing result metadata" - (:id saved-question) - (pr-str (:name saved-question-query))))) - (let [mbql-query (cond-> (assoc (lib.convert/->pMBQL mbql-query) - :lib/metadata (lib.metadata/->metadata-provider metadata-providerable)) - metadata - (lib.util/update-query-stage -1 assoc :lib/stage-metadata (lib.util/->stage-metadata metadata)))] - (query metadata-providerable mbql-query))) - (mu/defn query-from-legacy-inner-query :- ::lib.schema/query "Create a pMBQL query from a legacy inner query." [metadata-providerable :- lib.metadata/MetadataProviderable diff --git a/test/metabase/lib/card_test.cljc b/test/metabase/lib/card_test.cljc index 6132a224d6a..3be4736199b 100644 --- a/test/metabase/lib/card_test.cljc +++ b/test/metabase/lib/card_test.cljc @@ -1,5 +1,6 @@ (ns metabase.lib.card-test (:require + [clojure.set :as set] [clojure.test :refer [deftest is testing]] [metabase.lib.core :as lib] [metabase.lib.metadata.calculation :as lib.metadata.calculation] @@ -49,26 +50,22 @@ (lib/display-info query col)))))))) (deftest ^:parallel card-source-query-metadata-test - (doseq [metadata [{:id 1 - :name "My Card" - :result-metadata meta/card-results-metadata - :dataset-query {}} - ;; in some cases [the FE unit tests are broken] the FE is transforming the metadata like this, not sure why but handle it anyway + (doseq [metadata [(:venues lib.tu/mock-cards) + ;; in some cases [the FE unit tests are broken] the FE is transforming the metadata like this, not + ;; sure why but handle it anyway ;; (#29739) - {:id 1 - :name "My Card" - :fields meta/card-results-metadata - :dataset-query {}}]] + (set/rename-keys (:venues lib.tu/mock-cards) {:result-metadata :fields})]] (testing (str "metadata = \n" (u/pprint-to-str metadata)) (let [query {:lib/type :mbql/query :lib/metadata (lib.tu/mock-metadata-provider {:cards [metadata]}) :database (meta/id) :stages [{:lib/type :mbql.stage/mbql - :lib/options {:lib/uuid (str (random-uuid))} - :source-card 1}]}] - (is (=? (for [col meta/card-results-metadata] - (assoc col :lib/source :source/card)) + :source-card (:id metadata)}]}] + (is (=? (for [col (get-in lib.tu/mock-cards [:venues :result-metadata])] + (-> col + (assoc :lib/source :source/card) + (dissoc :fk-target-field-id))) (lib.metadata.calculation/returned-columns query))))))) (deftest ^:parallel card-results-metadata-merge-metadata-provider-metadata-test diff --git a/test/metabase/lib/column_group_test.cljc b/test/metabase/lib/column_group_test.cljc index 9880ab69e76..48f4005841e 100644 --- a/test/metabase/lib/column_group_test.cljc +++ b/test/metabase/lib/column_group_test.cljc @@ -275,31 +275,31 @@ (testing "#32509 when building a join against a Card" (doseq [{:keys [message card metadata-provider]} [{:message "MBQL Card" - :card lib.tu/categories-mbql-card - :metadata-provider lib.tu/metadata-provider-with-categories-mbql-card} + :card (:categories lib.tu/mock-cards) + :metadata-provider lib.tu/metadata-provider-with-mock-cards} {:message "Native Card" - :card lib.tu/categories-native-card - :metadata-provider lib.tu/metadata-provider-with-categories-native-card}]] + :card (lib.tu/mock-cards :categories/native) + :metadata-provider lib.tu/metadata-provider-with-mock-cards}]] (testing message (let [cols (rhs-columns lib.tu/venues-query card) groups (lib/group-columns cols)] (testing `lib/group-columns (is (=? [{:lib/type :metadata/column-group - :card-id 1 + :card-id (:id card) ::lib.column-group/group-type :group-type/join.explicit - ::lib.column-group/columns [{:name "ID", :lib/card-id 1} - {:name "NAME", :lib/card-id 1}]}] + ::lib.column-group/columns [{:name "ID", :lib/card-id (:id card)} + {:name "NAME", :lib/card-id (:id card)}]}] groups))) (testing `lib/display-info (testing "Card is not present in MetadataProvider" - (is (=? [{:display-name "Question 1" + (is (=? [{:display-name (str "Question " (:id card)) :is-from-join true}] (for [group groups] (lib/display-info lib.tu/venues-query group))))) (testing "Card *is* present in MetadataProvider" (let [query (assoc lib.tu/venues-query :lib/metadata metadata-provider)] - (is (=? [{:name "Tarot Card" - :display-name "Tarot Card" + (is (=? [{:name "Mock categories card" + :display-name "Mock Categories Card" :is-from-join true}] (for [group groups] (lib/display-info query group)))))))))))) diff --git a/test/metabase/lib/field_test.cljc b/test/metabase/lib/field_test.cljc index 552389bd8ff..353257da75b 100644 --- a/test/metabase/lib/field_test.cljc +++ b/test/metabase/lib/field_test.cljc @@ -21,9 +21,9 @@ #?(:cljs (comment metabase.test-runner.assert-exprs.approximately-equal/keep-me)) (deftest ^:parallel field-from-results-metadata-test - (let [field-metadata (lib.metadata/stage-column (lib/saved-question-query + (let [field-metadata (lib.metadata/stage-column (lib.tu/query-with-stage-metadata-from-card meta/metadata-provider - meta/saved-question) + (:venues lib.tu/mock-cards)) "ID")] (is (=? {:lib/type :metadata/column :name "ID"} @@ -151,9 +151,7 @@ ;; legacy result metadata will already include the Join name in the `:display-name`, so simulate that. Make ;; sure we're not including it twice. result-metadata (for [col (lib.metadata.calculation/returned-columns - (lib/saved-question-query - meta/metadata-provider - {:dataset-query card-query}))] + (lib/query meta/metadata-provider card-query))] (cond-> col (:source-alias col) (update :display-name (fn [display-name] @@ -459,18 +457,16 @@ (lib/display-info query)))))))) (deftest ^:parallel joined-field-column-name-test - (let [card {:dataset-query {:database (meta/id) - :type :query - :query {:source-table (meta/id :venues) - :joins [{:fields :all - :source-table (meta/id :categories) - :conditions [[:= - [:field (meta/id :venues :category-id) nil] - [:field (meta/id :categories :id) {:join-alias "Cat"}]]] - :alias "Cat"}]}}} - query (lib/saved-question-query - meta/metadata-provider - card)] + (let [legacy-query {:database (meta/id) + :type :query + :query {:source-table (meta/id :venues) + :joins [{:fields :all + :source-table (meta/id :categories) + :conditions [[:= + [:field (meta/id :venues :category-id) nil] + [:field (meta/id :categories :id) {:join-alias "Cat"}]]] + :alias "Cat"}]}} + query (lib/query meta/metadata-provider legacy-query)] (is (=? [{:lib/desired-column-alias "ID"} {:lib/desired-column-alias "NAME"} {:lib/desired-column-alias "CATEGORY_ID"} diff --git a/test/metabase/lib/filter_test.cljc b/test/metabase/lib/filter_test.cljc index 1fd28bb8f7f..a22042b57e3 100644 --- a/test/metabase/lib/filter_test.cljc +++ b/test/metabase/lib/filter_test.cljc @@ -8,12 +8,14 @@ [metabase.lib.test-util :as lib.tu] [metabase.lib.types.isa :as lib.types.isa])) +#?(:cljs (comment metabase.test-runner.assert-exprs.approximately-equal/keep-me)) + (defn- test-clause [result-filter f & args] (is (=? result-filter (apply f args)))) (deftest ^:parallel general-filter-clause-test - (let [q2 (lib/saved-question-query meta/metadata-provider meta/saved-question) + (let [q2 (lib.tu/query-with-stage-metadata-from-card meta/metadata-provider (:venues lib.tu/mock-cards)) venues-category-id-metadata (meta/field-metadata :venues :category-id) venues-name-metadata (meta/field-metadata :venues :name) venues-latitude-metadata (meta/field-metadata :venues :latitude) @@ -107,7 +109,7 @@ (deftest ^:parallel filter-test (let [q1 (lib/query meta/metadata-provider (meta/table-metadata :categories)) - q2 (lib/saved-question-query meta/metadata-provider meta/saved-question) + q2 (lib.tu/query-with-stage-metadata-from-card meta/metadata-provider (:venues lib.tu/mock-cards)) venues-category-id-metadata (meta/field-metadata :venues :category-id) original-filter [:between diff --git a/test/metabase/lib/join_test.cljc b/test/metabase/lib/join_test.cljc index 187f0ae22a2..dafa5e4fcf4 100644 --- a/test/metabase/lib/join_test.cljc +++ b/test/metabase/lib/join_test.cljc @@ -108,7 +108,7 @@ (meta/id :venues :category-id)]]]}]}]} (-> (lib/query meta/metadata-provider (meta/table-metadata :categories)) (lib/join (lib/join-clause - (lib/saved-question-query meta/metadata-provider meta/saved-question) + (lib.tu/query-with-stage-metadata-from-card meta/metadata-provider (:venues lib.tu/mock-cards)) [(lib/= (meta/field-metadata :categories :id) (meta/field-metadata :venues :category-id))])) (dissoc :lib/metadata))))) @@ -116,7 +116,7 @@ (deftest ^:parallel join-condition-field-metadata-test (testing "Should be able to use raw Field metadatas in the join condition" (let [q1 (lib/query meta/metadata-provider (meta/table-metadata :categories)) - q2 (lib/saved-question-query meta/metadata-provider meta/saved-question) + q2 (lib.tu/query-with-stage-metadata-from-card meta/metadata-provider (:venues lib.tu/mock-cards)) venues-category-id-metadata (meta/field-metadata :venues :category-id) categories-id-metadata (lib.metadata/stage-column q2 "ID")] (let [clause (lib/join-clause q2 [(lib/= categories-id-metadata venues-category-id-metadata)])] @@ -739,12 +739,12 @@ (testing "RHS columns when building a join against" (doseq [query [lib.tu/venues-query lib.tu/query-with-join] - [card-type card] {"Native" lib.tu/categories-native-card - "MBQL" lib.tu/categories-mbql-card}] + [card-type card] {"Native" (:categories/native lib.tu/mock-cards) + "MBQL" (:categories lib.tu/mock-cards)}] (testing (str "a " card-type " Card") (let [cols (lib/join-condition-rhs-columns query card nil nil)] - (is (=? [{:display-name "ID", :lib/source :source/joins, :lib/card-id 1} - {:display-name "Name", :lib/source :source/joins, :lib/card-id 1}] + (is (=? [{:display-name "ID", :lib/source :source/joins, :lib/card-id (:id card)} + {:display-name "Name", :lib/source :source/joins, :lib/card-id (:id card)}] cols)) (is (=? [{:display-name "ID", :is-from-join true} {:display-name "Name", :is-from-join true}] @@ -767,7 +767,7 @@ (let [query lib.tu/venues-query] (doseq [lhs [nil (lib.metadata/field query (meta/id :venues :id))] joined-thing [(meta/table-metadata :venues) - meta/saved-question-CardMetadata]] + (:venues lib.tu/mock-cards)]] (testing (str "lhs = " (pr-str lhs) "\njoined-thing = " (pr-str joined-thing)) (is (=? [{:lib/desired-column-alias "ID"} @@ -898,7 +898,7 @@ {:lib/type :metadata/column, :name "PRICE"}] (lib/joinable-columns lib.tu/venues-query -1 table-or-card)) (meta/table-metadata :venues) - meta/saved-question-CardMetadata)) + (:venues lib.tu/mock-cards))) (deftest ^:parallel joinable-columns-join-test (let [query lib.tu/query-with-join @@ -960,7 +960,7 @@ 2 (lib.tu/add-joins query "J1" "J2")} [first-join? join? join-or-joinable] (list* [(zero? num-existing-joins) false (meta/table-metadata :venues)] - [(zero? num-existing-joins) false meta/saved-question-CardMetadata] + [(zero? num-existing-joins) false (:venues lib.tu/mock-cards)] [(zero? num-existing-joins) false nil] (when-let [[first-join & more] (not-empty (lib/joins query))] (cons [true true first-join] diff --git a/test/metabase/lib/metadata/jvm_test.clj b/test/metabase/lib/metadata/jvm_test.clj index cb12b22cdd1..df9109f787f 100644 --- a/test/metabase/lib/metadata/jvm_test.clj +++ b/test/metabase/lib/metadata/jvm_test.clj @@ -25,18 +25,16 @@ (is (nil? (lib.metadata.protocols/database (lib.metadata.jvm/application-database-metadata-provider Integer/MAX_VALUE)))))) (deftest ^:parallel saved-question-metadata-test - (let [card {:dataset-query {:database (mt/id) - :type :query - :query {:source-table (mt/id :venues) - :joins [{:fields :all - :source-table (mt/id :categories) - :condition [:= - [:field (mt/id :venues :category_id) nil] - [:field (mt/id :categories :id) {:join-alias "Cat"}]] - :alias "Cat"}]}}} - query (lib/saved-question-query - (lib.metadata.jvm/application-database-metadata-provider (mt/id)) - card)] + (let [query {:database (mt/id) + :type :query + :query {:source-table (mt/id :venues) + :joins [{:fields :all + :source-table (mt/id :categories) + :condition [:= + [:field (mt/id :venues :category_id) nil] + [:field (mt/id :categories :id) {:join-alias "Cat"}]] + :alias "Cat"}]}} + query (lib/query (lib.metadata.jvm/application-database-metadata-provider (mt/id)) query)] (is (=? [{:lib/desired-column-alias "ID"} {:lib/desired-column-alias "NAME"} {:lib/desired-column-alias "CATEGORY_ID"} diff --git a/test/metabase/lib/metadata_test.cljc b/test/metabase/lib/metadata_test.cljc index d2325872b08..bb690600b98 100644 --- a/test/metabase/lib/metadata_test.cljc +++ b/test/metabase/lib/metadata_test.cljc @@ -8,6 +8,7 @@ [metabase.lib.test-util :as lib.tu] #?@(:cljs ([metabase.test-runner.assert-exprs.approximately-equal])))) +(comment lib/keep-me) #?(:cljs (comment metabase.test-runner.assert-exprs.approximately-equal/keep-me)) (deftest ^:parallel field-metadata-test @@ -17,7 +18,7 @@ (lib.metadata/field meta/metadata-provider (meta/id :venues :category-id))))) (deftest ^:parallel stage-metadata-test - (let [query (lib/saved-question-query meta/metadata-provider meta/saved-question)] + (let [query (lib.tu/query-with-stage-metadata-from-card meta/metadata-provider (:venues lib.tu/mock-cards))] (is (=? {:columns [{:name "ID"} {:name "NAME"} {:name "CATEGORY_ID"} @@ -27,9 +28,9 @@ (lib.metadata/stage query -1))))) (deftest ^:parallel stage-column-metadata-test - (let [query (lib/saved-question-query meta/metadata-provider meta/saved-question)] + (let [query (lib.tu/query-with-stage-metadata-from-card meta/metadata-provider (:venues lib.tu/mock-cards))] (are [x] (=? {:lib/type :metadata/column - :display-name "CATEGORY_ID" + :display-name "Category ID" :name "CATEGORY_ID" :base-type :type/Integer :effective-type :type/Integer diff --git a/test/metabase/lib/native_test.cljc b/test/metabase/lib/native_test.cljc index 29384c8eff9..f25b785ce9e 100644 --- a/test/metabase/lib/native_test.cljc +++ b/test/metabase/lib/native_test.cljc @@ -164,16 +164,29 @@ (is (= clj-tags (-> clj-tags (#'lib.native/TemplateTags->) (#'lib.native/->TemplateTags)))) (is (test.js/= js-tags (-> js-tags (#'lib.native/->TemplateTags) (#'lib.native/TemplateTags->)))))))) +(def ^:private qp-results-metadata + "Capture of the `data.results_metadata` that would come back when running `SELECT * FROM VENUES;` with the Query + Processor. + + IRL queries actually come back with both `data.cols` and `data.results_metadata.columns`, which are slightly + different from one another; the frontend merges these together into one unified metadata map. This is both icky and + silly. I'm hoping we can get away with just using one or the other in the future. So let's try to use just the stuff + here and see how far we get. If it turns out we need something in `data.cols` that's missing from here, let's just + add it to `data.results_metadata.columns` in QP results, and add it here as well, so we can start moving toward a + world where we don't have two versions of the metadata in query responses." + {:lib/type :metadata/results + :columns (get-in lib.tu/mock-cards [:venues :result-metadata])}) + (deftest ^:parallel native-query-test (is (=? {:lib/type :mbql/query :database (meta/id) :stages [{:lib/type :mbql.stage/native :lib/options {:lib/uuid string?} :native "SELECT * FROM VENUES;"}]} - (lib/native-query meta/metadata-provider "SELECT * FROM VENUES;" meta/qp-results-metadata nil)))) + (lib/native-query meta/metadata-provider "SELECT * FROM VENUES;" qp-results-metadata nil)))) (deftest ^:parallel native-query-suggested-name-test - (let [query (lib/native-query meta/metadata-provider "SELECT * FROM VENUES;" meta/qp-results-metadata nil)] + (let [query (lib/native-query meta/metadata-provider "SELECT * FROM VENUES;" qp-results-metadata nil)] (is (= "Native query" (lib.metadata.calculation/describe-query query))) (is (nil? (lib.metadata.calculation/suggested-name query))))) diff --git a/test/metabase/lib/query_test.cljc b/test/metabase/lib/query_test.cljc index 17a92756e23..7bda0551402 100644 --- a/test/metabase/lib/query_test.cljc +++ b/test/metabase/lib/query_test.cljc @@ -14,7 +14,8 @@ (deftest ^:parallel describe-query-test (let [query (-> lib.tu/venues-query (lib/aggregate (lib/sum (meta/field-metadata :venues :price)))) - ;; wrong arity: there's a bug in our Kondo config, see https://metaboat.slack.com/archives/C04DN5VRQM6/p1679022185079739?thread_ts=1679022025.317059&cid=C04DN5VRQM6 + ;; wrong arity: there's a bug in our Kondo config, see + ;; https://metaboat.slack.com/archives/C04DN5VRQM6/p1679022185079739?thread_ts=1679022025.317059&cid=C04DN5VRQM6 query (-> #_{:clj-kondo/ignore [:invalid-arity]} (lib/filter query (lib/= (meta/field-metadata :venues :name) "Toucannery")) (lib/breakout (meta/field-metadata :venues :category-id)) @@ -30,17 +31,6 @@ (lib.metadata.calculation/describe-query query) (lib.metadata.calculation/suggested-name query))))) -(deftest ^:parallel card-source-query-test - (is (=? {:lib/type :mbql/query - :database (meta/id) - :stages [{:lib/type :mbql.stage/native - :native "SELECT * FROM VENUES;"}]} - (lib/saved-question-query meta/metadata-provider - {:dataset-query {:database (meta/id) - :type :native - :native {:query "SELECT * FROM VENUES;"}} - :result-metadata meta/card-results-metadata})))) - (deftest ^:parallel notebook-query-test (is (=? {:lib/type :mbql/query :database (meta/id) diff --git a/test/metabase/lib/test_metadata.cljc b/test/metabase/lib/test_metadata.cljc index d6d7adbe957..90b21882d56 100644 --- a/test/metabase/lib/test_metadata.cljc +++ b/test/metabase/lib/test_metadata.cljc @@ -123,7 +123,7 @@ {:arglists '([table-name])} keyword) -(defmulti field-metadata-method +(defmulti ^:private field-metadata-method "Metadata for fields" {:arglists '([table-name field-name])} (fn [table-name field-name] @@ -186,13 +186,12 @@ :display-name "Name" :database-position 1 :database-required true - :fingerprint - {:global {:distinct-count 75, :nil% 0.0} - :type {:type/Text {:percent-json 0.0 - :percent-url 0.0 - :percent-email 0.0 - :percent-state 0.0 - :average-length 8.333333333333334}}} + :fingerprint {:global {:distinct-count 75, :nil% 0.0} + :type {:type/Text {:percent-json 0.0 + :percent-url 0.0 + :percent-email 0.0 + :percent-state 0.0 + :average-length 8.333333333333334}}} :base-type :type/Text :points-of-interest nil :lib/type :metadata/column}) @@ -2198,6 +2197,22 @@ "[[metabase.lib.metadata.protocols/MetadataProvider]] using the test [[metadata]]." (meta.graph-provider/->SimpleGraphMetadataProvider metadata)) +(mu/defn tables :- [:set :keyword] + "Set of valid table names." + [] + (set (keys (methods table-metadata-method)))) + +(mu/defn fields :- [:set :keyword] + "Set of valid table names for a `:table-name`." + [table-name :- :keyword] + (assert ((tables) table-name) + (str "Invalid table: " table-name)) + (into #{} + (keep (fn [[a-table-name a-field-name]] + (when (= a-table-name table-name) + a-field-name))) + (keys (methods field-metadata-method)))) + (mu/defn table-metadata :- lib.metadata/TableMetadata "Get Table metadata for a one of the `test-data` Tables in the test metadata, e.g. `:venues`. This is here so you can test things that should consume Table metadata." @@ -2210,113 +2225,3 @@ [table-name :- :keyword field-name :- :keyword] (field-metadata-method table-name field-name)) - -(def card-results-metadata - "Capture of the `result_metadata` saved with a Card with a `SELECT * FROM VENUES;` query. Actually this is a little - different because this is pre-massaged into the MLv2 shape (it has `:lib/type` and uses `kebab-case` keys), to make - it easier to use in tests. It should not make a difference because transformation to the MLv2 shape is idempotent... - and at some point we'll probably update the backend to store stuff in this shape anyway." - [{:lib/type :metadata/column - :display-name "ID" - :name "ID" - :base-type :type/BigInteger - :effective-type :type/BigInteger - :semantic-type :type/PK - :fingerprint nil} - {:lib/type :metadata/column - :display-name "NAME" ; TODO -- these display names are icky - :name "NAME" - :base-type :type/Text - :effective-type :type/Text - :semantic-type :type/Name - :fingerprint {:global {:distinct-count 100, :nil% 0.0} - :type {:type/Text {:percent-json 0.0 - :percent-url 0.0 - :percent-email 0.0 - :percent-state 0.0 - :average-length 15.63}}}} - {:lib/type :metadata/column - :display-name "CATEGORY_ID" - :name "CATEGORY_ID" - :base-type :type/Integer - :effective-type :type/Integer - :semantic-type :type/FK - :fingerprint {:global {:distinct-count 28, :nil% 0.0} - :type {:type/Number - {:min 2.0 - :q1 6.89564392373896 - :q3 49.240253073352044 - :max 74.0 - :sd 23.058108414099443 - :avg 29.98}}}} - {:lib/type :metadata/column - :display-name "LATITUDE" - :name "LATITUDE" - :base-type :type/Float - :effective-type :type/Float - :semantic-type :type/Latitude - :fingerprint - {:global {:distinct-count 94, :nil% 0.0} - :type {:type/Number {:min 10.0646 - :q1 34.06098873016278 - :q3 37.77185 - :max 40.7794 - :sd 3.4346725397190827 - :avg 35.505891999999996}}}} - {:lib/type :metadata/column - :display-name "LONGITUDE" - :name "LONGITUDE" - :base-type :type/Float - :effective-type :type/Float - :semantic-type :type/Longitude - :fingerprint {:global {:distinct-count 84, :nil% 0.0} - :type {:type/Number - {:min -165.374 - :q1 -122.40857106781186 - :q3 -118.2635 - :max -73.9533 - :sd 14.162810671348238 - :avg -115.99848699999998}}}} - {:lib/type :metadata/column - :display-name "PRICE" - :name "PRICE" - :base-type :type/Integer - :effective-type :type/Integer - :semantic-type nil - :fingerprint {:global {:distinct-count 4, :nil% 0.0} - :type {:type/Number - {:min 1.0 - :q1 1.4591129021415095 - :q3 2.493086095768049 - :max 4.0 - :sd 0.7713951678941896 - :avg 2.03}}}}]) - -(def qp-results-metadata - "Capture of the `data.results_metadata` that would come back when running `SELECT * FROM VENUES;` with the Query - Processor. - - IRL queries actually come back with both `data.cols` and `data.results_metadata.columns`, which are slightly - different from one another; the frontend merges these together into one unified metadata map. This is both icky and - silly. I'm hoping we can get away with just using one or the other in the future. So let's try to use just the stuff - here and see how far we get. If it turns out we need something in `data.cols` that's missing from here, let's just - add it to `data.results_metadata.columns` in QP results, and add it here as well, so we can start moving toward a - world where we don't have two versions of the metadata in query responses." - {:lib/type :metadata/results - :columns card-results-metadata}) - -(def saved-question - "An representative Saved Question, with [[results-metadata]], against `VENUES`. For testing queries that use a Saved - Question as their source. See also [[saved-question-CardMetadata]] below." - {:dataset-query {:database (id) - :type :query - :query {:source-table (id :venues)}} - :result-metadata card-results-metadata}) - -(def saved-question-CardMetadata - "Mock [[metabase.lib.metadata/CardMetadata]] with a query against `VENUES`. See - also [[metabase.lib.test-util/categories-mbql-card]] and [[metabase.lib.test-util/categories-native-card]]." - (assoc saved-question - :lib/type :metadata/card - :id 1 - :name "Card 1")) diff --git a/test/metabase/lib/test_util.cljc b/test/metabase/lib/test_util.cljc index 4c56664e047..22c8c866784 100644 --- a/test/metabase/lib/test_util.cljc +++ b/test/metabase/lib/test_util.cljc @@ -2,16 +2,18 @@ "Misc test utils for Metabase lib." (:require [clojure.core.protocols] - [clojure.test :refer [is]] + [clojure.test :refer [deftest is]] [malli.core :as mc] [medley.core :as m] + [metabase.lib.convert :as lib.convert] [metabase.lib.core :as lib] [metabase.lib.metadata :as lib.metadata] - [metabase.lib.metadata.composed-provider - :as lib.metadata.composed-provider] + [metabase.lib.metadata.composed-provider :as lib.metadata.composed-provider] [metabase.lib.metadata.protocols :as metadata.protocols] + [metabase.lib.query :as lib.query] [metabase.lib.schema :as lib.schema] [metabase.lib.test-metadata :as meta] + [metabase.lib.util :as lib.util] [metabase.util.malli :as mu] #?@(:cljs ([metabase.test-runner.assert-exprs.approximately-equal])))) @@ -97,8 +99,6 @@ (datafy [_this] (list `mock-metadata-provider m)))) - - (def metadata-provider-with-card "[[meta/metadata-provider]], but with a Card with ID 1." (lib.metadata.composed-provider/composed-metadata-provider @@ -226,69 +226,78 @@ :semantic-type :type/FK}]} :native "SELECT whatever"}]}) -(def categories-mbql-card - "Mock MBQL query Card against the `CATEGORIES` Table." - {:lib/type :metadata/card - :id 1 - :name "Tarot Card" - :dataset-query {:database (meta/id) - :type :query - :query {:source-table (meta/id :categories)}} - :result-metadata [(meta/field-metadata :categories :id) - (meta/field-metadata :categories :name)]}) - -(def metadata-provider-with-categories-mbql-card - "A metadata provider with the [[categories-mbql-card]] as Card 1. Composed with the - normal [[meta/metadata-provider]]." - (lib.metadata.composed-provider/composed-metadata-provider - meta/metadata-provider - (mock-metadata-provider - {:cards [categories-mbql-card]}))) - -(def categories-native-card - "Mock native query Card against the `CATEGORIES` Table." - {:lib/type :metadata/card - :id 1 - :name "Tarot Card" - :dataset-query {:database (meta/id) - :type :native - :native {:query "SELECT * FROM CATEGORIES;"}} - :result-metadata (mapv #(dissoc % :id :table-id) - [(meta/field-metadata :categories :id) - (meta/field-metadata :categories :name)])}) - -(def metadata-provider-with-categories-native-card - "A metadata provider with the [[categories-native-card]] as Card 1. Composed with the - normal [[meta/metadata-provider]]." - (lib.metadata.composed-provider/composed-metadata-provider - meta/metadata-provider - (mock-metadata-provider - {:cards [categories-native-card]}))) - (def mock-cards - "Map of mock MBQL query Card against the test tables." + "Map of mock MBQL query Card against the test tables. There are three versions of the Card for each table: + + * `:venues`, a Card WITH `:result-metadata` + * `:venues/no-metadata`, a Card WITHOUT `:result-metadata` + * `:venues/native`, a Card with `:result-metadata` and a NATIVE query." (into {} - (for [[idx table] (m/indexed [:categories - :checkins - :users - :venues - :products - :orders - :people - :reviews])] - [table {:lib/type :metadata/card - :id (inc idx) - :name (str "Mock " (name table) " card") - :dataset-query {:database (meta/id) - :type :query - :query {:source-table (meta/id table)}} - :result-metadata (for [[[meta-table meta-col] _] (methods meta/field-metadata-method) - :when (= table meta-table)] - (dissoc (meta/field-metadata table meta-col) :id :table-id))}]))) + (comp (mapcat (fn [table] + [{:table table, :metadata? true, :native? false, :card-name table} + {:table table, :metadata? true, :native? true, :card-name (keyword (name table) "native")} + {:table table, :metadata? false, :native? false, :card-name (keyword (name table) "no-metadata")}])) + (map-indexed (fn [idx {:keys [table metadata? native? card-name]}] + [card-name + (merge + {:lib/type :metadata/card + :id (inc idx) + :name (str "Mock " (name table) " card") + :dataset-query (if native? + {:database (meta/id) + :type :native + :native {:query (str "SELECT * FROM " (name table))}} + {:database (meta/id) + :type :query + :query {:source-table (meta/id table)}})} + (when metadata? + {:result-metadata + (->> (meta/fields table) + (map (partial meta/field-metadata table)) + (sort-by :id) + (mapv #(dissoc % :id :table-id)))}))]))) + (meta/tables))) (def metadata-provider-with-mock-cards "A metadata provider with all of the [[mock-cards]]. Composed with the normal [[meta/metadata-provider]]." (lib.metadata.composed-provider/composed-metadata-provider meta/metadata-provider (mock-metadata-provider - {:cards (vals mock-cards)}))) + {:cards (vals mock-cards)}))) + +(mu/defn query-with-mock-card-as-source-card :- [:and + ::lib.schema/query + [:map + [:stages [:tuple + [:map + [:source-card integer?]]]]]] + "Create a query with one of the [[mock-cards]] as its `:source-card`." + [table-name :- (into [:enum] (sort (keys mock-cards)))] + (lib/query metadata-provider-with-mock-cards (mock-cards table-name))) + +(mu/defn query-with-stage-metadata-from-card :- ::lib.schema/query + "Convenience for creating a query that has `:lib/metadata` stage metadata attached to it from a Card. Note that this + does not create a query with a `:source-card`. + + This is mostly around for historic reasons; consider using either [[metabase.lib.core/query]] + or [[query-with-mock-card-as-source-card]] instead, which are closer to real-life usage." + [metadata-providerable :- lib.metadata/MetadataProviderable + {mbql-query :dataset-query, metadata :result-metadata} :- [:map + [:dataset-query :map] + [:result-metadata [:sequential {:min 1} :map]]]] + (let [mbql-query (cond-> (assoc (lib.convert/->pMBQL mbql-query) + :lib/metadata (lib.metadata/->metadata-provider metadata-providerable)) + metadata + (lib.util/update-query-stage -1 assoc :lib/stage-metadata (lib.util/->stage-metadata metadata)))] + (lib.query/query metadata-providerable mbql-query))) + +(deftest ^:parallel card-source-query-test + (is (=? {:lib/type :mbql/query + :database (meta/id) + :stages [{:lib/type :mbql.stage/native + :native "SELECT * FROM VENUES;"}]} + (query-with-stage-metadata-from-card meta/metadata-provider + {:dataset-query {:database (meta/id) + :type :native + :native {:query "SELECT * FROM VENUES;"}} + :result-metadata (get-in mock-cards [:venues :result-metadata])})))) -- GitLab