From 425629de52385a01ad90a6f22bf8c76613e033de Mon Sep 17 00:00:00 2001 From: Darin Kelkhoff Date: Fri, 17 May 2024 15:58:53 -0500 Subject: [PATCH] Adding missing test --- .../Aggregate2DTableWidgetRendererTest.java | 96 ++++++++ .../dashboard/widgets/TableDataAssert.java | 215 ++++++++++++++++++ .../dashboard/widgets/TableDataRowAssert.java | 192 ++++++++++++++++ 3 files changed, 503 insertions(+) create mode 100644 qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/dashboard/widgets/Aggregate2DTableWidgetRendererTest.java create mode 100644 qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/dashboard/widgets/TableDataAssert.java create mode 100644 qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/dashboard/widgets/TableDataRowAssert.java diff --git a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/dashboard/widgets/Aggregate2DTableWidgetRendererTest.java b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/dashboard/widgets/Aggregate2DTableWidgetRendererTest.java new file mode 100644 index 00000000..d8c70032 --- /dev/null +++ b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/dashboard/widgets/Aggregate2DTableWidgetRendererTest.java @@ -0,0 +1,96 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2024. Kingsrook, LLC + * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States + * contact@kingsrook.com + * https://github.com/Kingsrook/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.kingsrook.qqq.backend.core.actions.dashboard.widgets; + + +import java.util.List; +import com.kingsrook.qqq.backend.core.BaseTest; +import com.kingsrook.qqq.backend.core.actions.tables.InsertAction; +import com.kingsrook.qqq.backend.core.exceptions.QException; +import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput; +import com.kingsrook.qqq.backend.core.model.actions.widgets.RenderWidgetInput; +import com.kingsrook.qqq.backend.core.model.actions.widgets.RenderWidgetOutput; +import com.kingsrook.qqq.backend.core.model.dashboard.widgets.TableData; +import com.kingsrook.qqq.backend.core.model.data.QRecord; +import com.kingsrook.qqq.backend.core.model.metadata.dashboard.QWidgetMetaData; +import com.kingsrook.qqq.backend.core.utils.TestUtils; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; + + +/******************************************************************************* + ** Unit test for Aggregate2DTableWidgetRenderer + *******************************************************************************/ +class Aggregate2DTableWidgetRendererTest extends BaseTest +{ + + /******************************************************************************* + ** + *******************************************************************************/ + @Test + void test() throws QException + { + new InsertAction().execute(new InsertInput(TestUtils.TABLE_NAME_PERSON_MEMORY).withRecords(List.of( + new QRecord().withValue("lastName", "Simpson").withValue("homeStateId", 50), + new QRecord().withValue("lastName", "Simpson").withValue("homeStateId", 50), + new QRecord().withValue("lastName", "Simpson").withValue("homeStateId", 50), + new QRecord().withValue("lastName", "Simpson").withValue("homeStateId", 49), + new QRecord().withValue("lastName", "Flanders").withValue("homeStateId", 49), + new QRecord().withValue("lastName", "Flanders").withValue("homeStateId", 49), + new QRecord().withValue("lastName", "Burns").withValue("homeStateId", 50) + ))); + + RenderWidgetInput input = new RenderWidgetInput(); + input.setWidgetMetaData(new QWidgetMetaData() + .withDefaultValue("tableName", TestUtils.TABLE_NAME_PERSON_MEMORY) + .withDefaultValue("valueField", "id") + .withDefaultValue("rowField", "lastName") + .withDefaultValue("columnField", "homeStateId") + .withDefaultValue("orderBys", "row") + ); + RenderWidgetOutput output = new Aggregate2DTableWidgetRenderer().render(input); + TableData tableData = (TableData) output.getWidgetData(); + System.out.println(tableData.getRows()); + + TableDataAssert.assertThat(tableData) + .hasRowWithColumnContaining("_row", "Simpson", row -> + row.hasColumnContaining("50", "3") + .hasColumnContaining("49", "1") + .hasColumnContaining("_total", "4")) + .hasRowWithColumnContaining("_row", "Flanders", row -> + row.hasColumnContaining("50", "0") + .hasColumnContaining("49", "2") + .hasColumnContaining("_total", "2")) + .hasRowWithColumnContaining("_row", "Burns", row -> + row.hasColumnContaining("50", "1") + .hasColumnContaining("49", "0") + .hasColumnContaining("_total", "1")) + .hasRowWithColumnContaining("_row", "Total", row -> + row.hasColumnContaining("50", "4") + .hasColumnContaining("49", "3") + .hasColumnContaining("_total", "7")); + + List rowLabels = tableData.getRows().stream().map(r -> r.get("_row").toString()).toList(); + assertEquals(List.of("Burns", "Flanders", "Simpson", "Total"), rowLabels); + } + +} \ No newline at end of file diff --git a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/dashboard/widgets/TableDataAssert.java b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/dashboard/widgets/TableDataAssert.java new file mode 100644 index 00000000..5724f815 --- /dev/null +++ b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/dashboard/widgets/TableDataAssert.java @@ -0,0 +1,215 @@ +/* + * Copyright © 2022-2023. ColdTrack . All Rights Reserved. + */ + +package com.kingsrook.qqq.backend.core.actions.dashboard.widgets; + + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.Consumer; +import java.util.function.Predicate; +import com.kingsrook.qqq.backend.core.model.actions.widgets.RenderWidgetOutput; +import com.kingsrook.qqq.backend.core.model.dashboard.widgets.QWidgetData; +import com.kingsrook.qqq.backend.core.model.dashboard.widgets.TableData; +import com.kingsrook.qqq.backend.core.utils.StringUtils; +import org.assertj.core.api.AbstractAssert; +import org.assertj.core.api.Assertions; + + +/******************************************************************************* + ** AssertJ assert class for widget TableData + *******************************************************************************/ +public class TableDataAssert extends AbstractAssert +{ + + /******************************************************************************* + ** + *******************************************************************************/ + protected TableDataAssert(TableData actual, Class selfType) + { + super(actual, selfType); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public static TableDataAssert assertThat(RenderWidgetOutput widgetOutput) + { + Assertions.assertThat(widgetOutput).isNotNull(); + QWidgetData widgetData = widgetOutput.getWidgetData(); + Assertions.assertThat(widgetData).isNotNull(); + Assertions.assertThat(widgetData).isInstanceOf(TableData.class); + return (new TableDataAssert((TableData) widgetData, TableDataAssert.class)); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public static TableDataAssert assertThat(TableData actual) + { + return (new TableDataAssert(actual, TableDataAssert.class)); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public TableDataAssert hasSize(int expectedSize) + { + Assertions.assertThat(actual.getRows()).hasSize(expectedSize); + return (this); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public TableDataAssert hasSizeAtLeast(int sizeAtLeast) + { + Assertions.assertThat(actual.getRows()).hasSizeGreaterThanOrEqualTo(sizeAtLeast); + return (this); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public TableDataAssert doesNotHaveRowWithColumnContaining(String columnName, String containingValue) + { + for(Map row : actual.getRows()) + { + if(row.containsKey(columnName)) + { + String value = String.valueOf(row.get(columnName)); + if(value != null && value.contains(containingValue)) + { + failWithMessage("Failed because a row was found with a value in the [" + columnName + "] column containing [" + containingValue + "]" + + (containingValue.equals(value) ? "" : " (full value: [" + value + "]).")); + } + } + } + + return (this); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public TableDataAssert hasRowWithColumnContaining(String columnName, String containingValue) + { + hasRowWithColumnContaining(columnName, containingValue, (row) -> + { + }); + return (this); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public TableDataAssert hasRowWithColumnContaining(String columnName, String containingValue, Consumer rowAsserter) + { + return hasRowWithColumnPredicate(columnName, value -> value != null && value.contains(containingValue), "containing [" + containingValue + "]", rowAsserter); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public TableDataAssert hasRowWithColumnMatching(String columnName, String matchingValue) + { + hasRowWithColumnMatching(columnName, matchingValue, (row) -> + { + }); + return (this); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public TableDataAssert hasRowWithColumnMatching(String columnName, String matchingValue, Consumer rowAsserter) + { + return hasRowWithColumnPredicate(columnName, value -> value != null && value.matches(matchingValue), "matching [" + matchingValue + "]", rowAsserter); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public TableDataAssert hasRowWithColumnEqualTo(String columnName, String equalToValue) + { + hasRowWithColumnEqualTo(columnName, equalToValue, (row) -> + { + }); + return (this); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public TableDataAssert hasRowWithColumnEqualTo(String columnName, String equalToValue, Consumer rowAsserter) + { + return hasRowWithColumnPredicate(columnName, value -> Objects.equals(value, equalToValue), "equalTo [" + equalToValue + "]", rowAsserter); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + private TableDataAssert hasRowWithColumnPredicate(String columnName, Predicate predicate, String predicateDescription, Consumer rowAsserter) + { + List foundValuesInColumn = new ArrayList<>(); + for(Map row : actual.getRows()) + { + if(row.containsKey(columnName)) + { + String value = String.valueOf(row.get(columnName)); + foundValuesInColumn.add(value); + + if(predicate.test(value)) + { + TableDataRowAssert tableDataRowAssert = TableDataRowAssert.assertThat(row); + rowAsserter.accept(tableDataRowAssert); + + return (this); + } + } + } + + if(actual.getRows().isEmpty()) + { + failWithMessage("Failed because there are no rows in the table."); + } + else if(foundValuesInColumn.isEmpty()) + { + failWithMessage("Failed to find any rows with a column named: [" + columnName + "]"); + } + else + { + failWithMessage("Failed to find a row with column [" + columnName + "] " + predicateDescription + + ".\nFound values were:\n" + StringUtils.join("\n", foundValuesInColumn)); + } + return (null); + } + +} diff --git a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/dashboard/widgets/TableDataRowAssert.java b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/dashboard/widgets/TableDataRowAssert.java new file mode 100644 index 00000000..83ecf3ad --- /dev/null +++ b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/dashboard/widgets/TableDataRowAssert.java @@ -0,0 +1,192 @@ +/* + * Copyright © 2022-2023. ColdTrack . All Rights Reserved. + */ + +package com.kingsrook.qqq.backend.core.actions.dashboard.widgets; + + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; +import java.util.function.Function; +import com.kingsrook.qqq.backend.core.utils.StringUtils; +import com.kingsrook.qqq.backend.core.utils.ValueUtils; +import org.assertj.core.api.AbstractAssert; +import org.assertj.core.api.Assertions; +import static org.junit.jupiter.api.Assertions.fail; + + +/******************************************************************************* + ** AssertJ assert class for a row of data from a widget TableData + *******************************************************************************/ +public class TableDataRowAssert extends AbstractAssert> +{ + + /******************************************************************************* + ** + *******************************************************************************/ + protected TableDataRowAssert(Map actual, Class selfType) + { + super(actual, selfType); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public static TableDataRowAssert assertThat(Map actual) + { + return (new TableDataRowAssert(actual, TableDataRowAssert.class)); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public TableDataRowAssert hasColumnContaining(String columnName, String containingValue) + { + String value = String.valueOf(actual.get(columnName)); + Assertions.assertThat(value) + .withFailMessage("Expected column [" + columnName + "] in row [" + actual + "] to contain [" + containingValue + "], but it didn't") + .contains(containingValue); + return (this); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public TableDataRowAssert hasNoSubRows() + { + Object subRowsObject = actual.get("subRows"); + if(subRowsObject != null) + { + @SuppressWarnings("unchecked") + List> subRowsList = (List>) subRowsObject; + if(!subRowsList.isEmpty()) + { + fail("Row [" + actual + "] should not have had any subRows, but it did."); + } + } + + return (this); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public TableDataRowAssert hasSubRowWithColumnContaining(String columnName, String containingValue) + { + hasSubRowWithColumnContaining(columnName, containingValue, (row) -> + { + }); + + return (this); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + private TableDataRowAssert hasSubRowWithColumnPredicate(String columnName, Function predicate, String predicateDescription, Consumer rowAsserter) + { + Object subRowsObject = actual.get("subRows"); + Assertions.assertThat(subRowsObject) + .withFailMessage("subRows should not be null").isNotNull() + .withFailMessage("subRows should be a List").isInstanceOf(List.class); + + @SuppressWarnings("unchecked") + List> subRowsList = (List>) subRowsObject; + + List foundValuesInColumn = new ArrayList<>(); + for(Map row : subRowsList) + { + if(row.containsKey(columnName)) + { + String value = String.valueOf(row.get(columnName)); + foundValuesInColumn.add(value); + + if(value != null && predicate.apply(value)) + { + TableDataRowAssert tableDataRowAssert = TableDataRowAssert.assertThat(row); + rowAsserter.accept(tableDataRowAssert); + + return (this); + } + } + } + + if(foundValuesInColumn.isEmpty()) + { + failWithMessage("Failed to find any rows with a column named: [" + columnName + "]"); + } + else + { + failWithMessage("Failed to find a row with column [" + columnName + "] " + predicateDescription + + ".\nFound values were:\n" + StringUtils.join("\n", foundValuesInColumn)); + } + return (null); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public TableDataRowAssert hasSubRowWithColumnMatching(String columnName, String matchesValue, Consumer rowAsserter) + { + Function predicate = (value) -> ValueUtils.getValueAsString(value).matches(matchesValue); + return hasSubRowWithColumnPredicate(columnName, predicate, " matching [" + matchesValue + "]", rowAsserter); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public TableDataRowAssert hasSubRowWithColumnContaining(String columnName, String containingValue, Consumer rowAsserter) + { + Function predicate = (value) -> ValueUtils.getValueAsString(value).contains(containingValue); + return hasSubRowWithColumnPredicate(columnName, predicate, " containing [" + containingValue + "]", rowAsserter); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public TableDataRowAssert doesNotHaveSubRowWithColumnContaining(String columnName, String containingValue) + { + Object subRowsObject = actual.get("subRows"); + if(subRowsObject != null) + { + Assertions.assertThat(subRowsObject).withFailMessage("subRows should be a List").isInstanceOf(List.class); + + @SuppressWarnings("unchecked") + List> subRowsList = (List>) subRowsObject; + + for(Map row : subRowsList) + { + if(row.containsKey(columnName)) + { + String value = String.valueOf(row.get(columnName)); + if(value != null && value.contains(containingValue)) + { + failWithMessage("Failed because a row was found with a value in the [" + columnName + "] column containing [" + containingValue + "]" + + (containingValue.equals(value) ? "" : " (full value: [" + value + "]).")); + } + } + } + } + + return (this); + } + +}