mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-27 20:44:07 +00:00
8644f346af
Summary: Previously, when a table was deleted, ref/reflist columns pointing at that table were deleted as well. This diff changes that, converting the columns to a suitable type instead. See here for discussion and decisions: https://grist.slack.com/archives/C069RUP71/p1686060034848139 Test Plan: Added Python tests for most cases, and a DocApi test for where Python has to call JS code to convert the values. Reviewers: paulfitz Reviewed By: paulfitz Differential Revision: https://phab.getgrist.com/D4011
491 lines
21 KiB
Python
491 lines
21 KiB
Python
import logging
|
|
|
|
import testutil
|
|
import test_engine
|
|
from test_engine import Table, Column, View, Section, Field
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
class TestTableActions(test_engine.EngineTestCase):
|
|
|
|
address_table_data = [
|
|
["id", "city", "state", "amount" ],
|
|
[ 21, "New York", "NY" , 1. ],
|
|
[ 22, "Albany", "NY" , 2. ],
|
|
[ 23, "Seattle", "WA" , 3. ],
|
|
[ 24, "Chicago", "IL" , 4. ],
|
|
[ 25, "Bedford", "MA" , 5. ],
|
|
[ 26, "New York", "NY" , 6. ],
|
|
[ 27, "Buffalo", "NY" , 7. ],
|
|
[ 28, "Bedford", "NY" , 8. ],
|
|
[ 29, "Boston", "MA" , 9. ],
|
|
[ 30, "Yonkers", "NY" , 10. ],
|
|
[ 31, "New York", "NY" , 11. ],
|
|
]
|
|
|
|
people_table_data = [
|
|
["id", "name", "address" ],
|
|
[ 1, "Alice", 22 ],
|
|
[ 2, "Bob", 25 ],
|
|
[ 3, "Carol", 27 ],
|
|
]
|
|
|
|
def init_sample_data(self):
|
|
# Add a couple of tables, including references.
|
|
self.apply_user_action(["AddTable", "Address", [
|
|
{"id": "city", "type": "Text"},
|
|
{"id": "state", "type": "Text"},
|
|
{"id": "amount", "type": "Numeric"},
|
|
]])
|
|
self.apply_user_action(["AddTable", "People", [
|
|
{"id": "name", "type": "Text"},
|
|
{"id": "address", "type": "Ref:Address"},
|
|
{"id": "city", "type": "Any", "formula": "$address.city" }
|
|
]])
|
|
|
|
# Populate some data.
|
|
d = testutil.table_data_from_rows("Address", self.address_table_data[0],
|
|
self.address_table_data[1:])
|
|
self.apply_user_action(["BulkAddRecord", "Address", d.row_ids, d.columns])
|
|
|
|
d = testutil.table_data_from_rows("People", self.people_table_data[0],
|
|
self.people_table_data[1:])
|
|
self.apply_user_action(["BulkAddRecord", "People", d.row_ids, d.columns])
|
|
|
|
# Add a view with several sections, including a summary table.
|
|
self.apply_user_action(["CreateViewSection", 1, 0, 'record', None, None])
|
|
self.apply_user_action(["CreateViewSection", 1, 3, 'record', [3], None])
|
|
self.apply_user_action(["CreateViewSection", 2, 3, 'record', None, None])
|
|
|
|
# Verify the new structure of tables and views.
|
|
self.assertTables([
|
|
Table(1, "Address", primaryViewId=1, summarySourceTable=0, columns=[
|
|
Column(1, "manualSort", "ManualSortPos", False, "", 0),
|
|
Column(2, "city", "Text", False, "", 0),
|
|
Column(3, "state", "Text", False, "", 0),
|
|
Column(4, "amount", "Numeric", False, "", 0),
|
|
]),
|
|
Table(2, "People", primaryViewId=2, summarySourceTable=0, columns=[
|
|
Column(5, "manualSort", "ManualSortPos", False, "", 0),
|
|
Column(6, "name", "Text", False, "", 0),
|
|
Column(7, "address", "Ref:Address", False, "", 0),
|
|
Column(8, "city", "Any", True, "$address.city", 0),
|
|
]),
|
|
Table(3, "Address_summary_state", 0, 1, columns=[
|
|
Column(9, "state", "Text", False, "", summarySourceCol=3),
|
|
Column(10, "group", "RefList:Address", True, summarySourceCol=0,
|
|
formula="table.getSummarySourceGroup(rec)"),
|
|
Column(11, "count", "Int", True, summarySourceCol=0, formula="len($group)"),
|
|
Column(12, "amount", "Numeric", True, summarySourceCol=0, formula="SUM($group.amount)"),
|
|
]),
|
|
])
|
|
self.assertViews([
|
|
View(1, sections=[
|
|
Section(1, parentKey="record", tableRef=1, fields=[
|
|
Field(1, colRef=2),
|
|
Field(2, colRef=3),
|
|
Field(3, colRef=4),
|
|
]),
|
|
]),
|
|
View(2, sections=[
|
|
Section(3, parentKey="record", tableRef=2, fields=[
|
|
Field(7, colRef=6),
|
|
Field(8, colRef=7),
|
|
Field(9, colRef=8),
|
|
]),
|
|
]),
|
|
View(3, sections=[
|
|
Section(5, parentKey="record", tableRef=1, fields=[
|
|
Field(13, colRef=2),
|
|
Field(14, colRef=3),
|
|
Field(15, colRef=4),
|
|
]),
|
|
Section(7, parentKey="record", tableRef=3, fields=[
|
|
Field(19, colRef=9),
|
|
Field(20, colRef=11),
|
|
Field(21, colRef=12),
|
|
]),
|
|
Section(8, parentKey="record", tableRef=2, fields=[
|
|
Field(22, colRef=6),
|
|
Field(23, colRef=7),
|
|
Field(24, colRef=8),
|
|
]),
|
|
]),
|
|
])
|
|
|
|
# Verify the data we've loaded.
|
|
self.assertTableData('Address', cols="subset", data=self.address_table_data)
|
|
self.assertTableData('People', cols="subset", data=self.people_table_data)
|
|
self.assertTableData("Address_summary_state", cols="subset", data=[
|
|
[ "id", "state", "count", "amount" ],
|
|
[ 1, "NY", 7, 1.+2+6+7+8+10+11 ],
|
|
[ 2, "WA", 1, 3. ],
|
|
[ 3, "IL", 1, 4. ],
|
|
[ 4, "MA", 2, 5.+9 ],
|
|
])
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
@test_engine.test_undo
|
|
def test_table_updates(self):
|
|
# Verify table renames triggered by UpdateRecord actions, and related behavior.
|
|
|
|
# Load a sample with a few table and views.
|
|
self.init_sample_data()
|
|
|
|
# Verify that we can rename tables via UpdatRecord actions, including multiple tables.
|
|
self.apply_user_action(["BulkUpdateRecord", "_grist_Tables", [1,2],
|
|
{"tableId": ["Location", "Persons"]}])
|
|
|
|
# Check that requested tables and summary tables got renamed correctly.
|
|
self.assertTableData('_grist_Tables', cols="subset", data=[
|
|
["id", "tableId"],
|
|
[1, "Location"],
|
|
[2, "Persons"],
|
|
[3, "Location_summary_state"],
|
|
])
|
|
|
|
# Check that reference columns to renamed tables get their type modified.
|
|
self.assertTableData('_grist_Tables_column', rows="subset", cols="subset", data=[
|
|
["id", "colId", "type"],
|
|
[7, "address", "Ref:Location"],
|
|
[10, "group", "RefList:Location"],
|
|
])
|
|
|
|
# Do a bulk update to rename A and B to conflicting names.
|
|
self.apply_user_action(["AddTable", "A", [{"id": "a", "type": "Text"}]])
|
|
out_actions = self.apply_user_action(["BulkUpdateRecord", "_grist_Tables", [1,2],
|
|
{"tableId": ["A", "A"]}])
|
|
|
|
# See what doc-actions get generated.
|
|
self.assertPartialOutActions(out_actions, {
|
|
"stored": [
|
|
["ModifyColumn", "Persons", "address", {"type": "Int"}],
|
|
["ModifyColumn", "Location_summary_state", "group", {"type": "Int"}],
|
|
["RenameTable", "Location", "A2"],
|
|
["RenameTable", "Location_summary_state", "A2_summary_state"],
|
|
["RenameTable", "Persons", "A3"],
|
|
["BulkUpdateRecord", "_grist_Tables", [1, 3, 2],
|
|
{"tableId": ["A2", "A2_summary_state", "A3"]}],
|
|
["ModifyColumn", "A3", "address", {"type": "Ref:A2"}],
|
|
["ModifyColumn", "A2_summary_state", "group", {"type": "RefList:A2"}],
|
|
["BulkUpdateRecord", "_grist_Tables_column", [7, 10], {"type": ["Ref:A2", "RefList:A2"]}],
|
|
]
|
|
})
|
|
|
|
# Check that requested tables and summary tables got renamed correctly.
|
|
self.assertTableData('_grist_Tables', cols="subset", data=[
|
|
["id", "tableId"],
|
|
[1, "A2"],
|
|
[2, "A3"],
|
|
[3, "A2_summary_state"],
|
|
[4, "A"],
|
|
])
|
|
|
|
# Check that reference columns to renamed tables get their type modified.
|
|
self.assertTableData('_grist_Tables_column', rows="subset", cols="subset", data=[
|
|
["id", "colId", "type"],
|
|
[7, "address", "Ref:A2"],
|
|
[10, "group", "RefList:A2"],
|
|
])
|
|
|
|
# Verify the data we've loaded.
|
|
self.assertTableData('A2', cols="subset", data=self.address_table_data)
|
|
self.assertTableData('A3', cols="subset", data=self.people_table_data)
|
|
self.assertTableData("A2_summary_state", cols="subset", data=[
|
|
[ "id", "state", "count", "amount" ],
|
|
[ 1, "NY", 7, 1.+2+6+7+8+10+11 ],
|
|
[ 2, "WA", 1, 3. ],
|
|
[ 3, "IL", 1, 4. ],
|
|
[ 4, "MA", 2, 5.+9 ],
|
|
])
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
@test_engine.test_undo
|
|
def test_table_renames_summary_by_ref(self):
|
|
# Verify table renames when there is a group-by column that's a Reference.
|
|
|
|
# This tests a potential bug since a table rename needs to modify Reference types, but
|
|
# group-by columns aren't supposed to be modifiable.
|
|
self.init_sample_data()
|
|
|
|
# Add a table grouped by a reference column (the 'Ref:Address' column named 'address').
|
|
self.apply_user_action(["CreateViewSection", 2, 0, 'record', [7], None])
|
|
self.assertTableData('_grist_Tables_column', cols="subset", data=[
|
|
["id", "colId", "type", "isFormula", "formula" ],
|
|
[ 13, "address", "Ref:Address", False, "" ],
|
|
[ 14, "group", "RefList:People", True, "table.getSummarySourceGroup(rec)" ],
|
|
[ 15, "count", "Int", True, "len($group)" ],
|
|
], rows=lambda r: (r.parentId.id == 4))
|
|
|
|
# Now rename the table Address -> Location.
|
|
out_actions = self.apply_user_action(["RenameTable", "Address", "Location"])
|
|
|
|
# See what doc-actions get generated.
|
|
self.assertPartialOutActions(out_actions, {
|
|
"stored": [
|
|
["ModifyColumn", "People", "address", {"type": "Int"}],
|
|
["ModifyColumn", "Address_summary_state", "group", {"type": "Int"}],
|
|
["ModifyColumn", "People_summary_address", "address", {"type": "Int"}],
|
|
["RenameTable", "Address", "Location"],
|
|
["RenameTable", "Address_summary_state", "Location_summary_state"],
|
|
["BulkUpdateRecord", "_grist_Tables", [1, 3],
|
|
{"tableId": ["Location", "Location_summary_state"]}],
|
|
["ModifyColumn", "People", "address", {"type": "Ref:Location"}],
|
|
["ModifyColumn", "Location_summary_state", "group", {"type": "RefList:Location"}],
|
|
["ModifyColumn", "People_summary_address", "address", {"type": "Ref:Location"}],
|
|
["BulkUpdateRecord", "_grist_Tables_column", [7, 10, 13],
|
|
{"type": ["Ref:Location", "RefList:Location", "Ref:Location"]}],
|
|
]
|
|
})
|
|
|
|
self.assertTableData('_grist_Tables_column', cols="subset", data=[
|
|
["id", "colId", "type", "isFormula", "formula" ],
|
|
[ 13, "address", "Ref:Location", False, "" ],
|
|
[ 14, "group", "RefList:People", True, "table.getSummarySourceGroup(rec)" ],
|
|
[ 15, "count", "Int", True, "len($group)" ],
|
|
], rows=lambda r: (r.parentId.id == 4))
|
|
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
@test_engine.test_undo
|
|
def test_table_removes(self):
|
|
# Verify table removals triggered by UpdateRecord actions, and related behavior.
|
|
|
|
# Same setup as previous test.
|
|
self.init_sample_data()
|
|
|
|
# Add one more table, and one more view for tables #1 and #4 (those we are about to delete).
|
|
self.apply_user_action(["AddEmptyTable", None])
|
|
out_actions = self.apply_user_action(["CreateViewSection", 1, 0, 'detail', None, None])
|
|
self.assertEqual(out_actions.retValues[0]["viewRef"], 5)
|
|
self.apply_user_action(["CreateViewSection", 4, 5, 'detail', None, None])
|
|
|
|
# See what's in TabBar table, to verify after we remove a table.
|
|
self.assertTableData('_grist_TabBar', cols="subset", data=[
|
|
["id", "viewRef"],
|
|
[1, 1],
|
|
[2, 2],
|
|
[3, 3],
|
|
[4, 4],
|
|
[5, 5],
|
|
])
|
|
|
|
# Remove two tables, ensure certain views get removed.
|
|
self.apply_user_action(["BulkRemoveRecord", "_grist_Tables", [1, 4]])
|
|
|
|
# See that some TabBar entries disappear, or tableRef gets unset.
|
|
self.assertTableData('_grist_TabBar', cols="subset", data=[
|
|
["id", "viewRef"],
|
|
[2, 2],
|
|
[3, 3],
|
|
])
|
|
|
|
# Check that reference columns to this table get converted
|
|
self.assertTables([
|
|
Table(2, "People", primaryViewId=2, summarySourceTable=0, columns=[
|
|
Column(5, "manualSort", "ManualSortPos", False, "", 0),
|
|
Column(6, "name", "Text", False, "", 0),
|
|
Column(7, "address", "Int", False, "", 0),
|
|
Column(8, "city", "Any", True, "$address.city", 0),
|
|
]),
|
|
# Note that the summary table is also gone.
|
|
])
|
|
self.assertViews([
|
|
View(2, sections=[
|
|
Section(3, parentKey="record", tableRef=2, fields=[
|
|
Field(7, colRef=6),
|
|
Field(8, colRef=7),
|
|
Field(9, colRef=8),
|
|
]),
|
|
]),
|
|
View(3, sections=[
|
|
Section(8, parentKey="record", tableRef=2, fields=[
|
|
Field(22, colRef=6),
|
|
Field(23, colRef=7),
|
|
Field(24, colRef=8),
|
|
]),
|
|
]),
|
|
])
|
|
|
|
@test_engine.test_undo
|
|
def test_remove_table_referenced_by_formula(self):
|
|
self.init_sample_data()
|
|
|
|
# Add a simple formula column of reference type.
|
|
# Its type will be changed to Any.
|
|
self.add_column(
|
|
"People", "address2", type="Ref:Address", isFormula=True, formula="$address"
|
|
)
|
|
# Add a similar reflist column, but change it to a data column.
|
|
# The formula is just an easy way to populate values for the test.
|
|
# A data column of type reflist should be changed to text.
|
|
self.add_column(
|
|
"People", "addresses", type="RefList:Address", isFormula=True, formula="[$address, $address]"
|
|
)
|
|
self.modify_column("People", "addresses", isFormula=False)
|
|
|
|
# Now remove the referenced table and see what happens to the ref columns.
|
|
self.apply_user_action(["RemoveTable", "Address"])
|
|
self.assertTables([
|
|
Table(2, "People", primaryViewId=2, summarySourceTable=0, columns=[
|
|
Column(5, "manualSort", "ManualSortPos", False, "", 0),
|
|
Column(6, "name", "Text", False, "", 0),
|
|
# Original data column of type Ref:Address is changed to Int.
|
|
Column(7, "address", "Int", False, "", 0),
|
|
Column(8, "city", "Any", True, "$address.city", 0),
|
|
# Formula column of type Ref:Address is changed to Any.
|
|
Column(13, "address2", "Any", True, "$address", 0),
|
|
# Data column of type RefList:Address is changed to Text.
|
|
Column(14, "addresses", "Text", False, "[$address, $address]", 0),
|
|
]),
|
|
])
|
|
self.assertTableData('People', cols="subset", data=[
|
|
["id", "name", "address", "address2", "addresses"],
|
|
[ 1, "Alice", 22, 22, "22,22"],
|
|
[ 2, "Bob", 25, 25, "25,25"],
|
|
[ 3, "Carol", 27, 27, "27,27"],
|
|
])
|
|
|
|
@test_engine.test_undo
|
|
def test_remove_table_referenced_by_summary_groupby_col_without_visible_col(self):
|
|
self.init_sample_data()
|
|
# Create a summary table of People grouped by address (a reference column).
|
|
self.apply_user_action(["CreateViewSection", 2, 0, 'record', [7], None])
|
|
|
|
self.assertTables([
|
|
Table(1, "Address", primaryViewId=1, summarySourceTable=0, columns=[
|
|
Column(1, "manualSort", "ManualSortPos", False, "", 0),
|
|
Column(2, "city", "Text", False, "", 0),
|
|
Column(3, "state", "Text", False, "", 0),
|
|
Column(4, "amount", "Numeric", False, "", 0),
|
|
]),
|
|
Table(2, "People", primaryViewId=2, summarySourceTable=0, columns=[
|
|
Column(5, "manualSort", "ManualSortPos", False, "", 0),
|
|
Column(6, "name", "Text", False, "", 0),
|
|
Column(7, "address", "Ref:Address", False, "", 0),
|
|
Column(8, "city", "Any", True, "$address.city", 0),
|
|
]),
|
|
Table(3, "Address_summary_state", 0, 1, columns=[
|
|
Column(9, "state", "Text", False, "", summarySourceCol=3),
|
|
Column(10, "group", "RefList:Address", True, summarySourceCol=0,
|
|
formula="table.getSummarySourceGroup(rec)"),
|
|
Column(11, "count", "Int", True, summarySourceCol=0, formula="len($group)"),
|
|
Column(12, "amount", "Numeric", True, summarySourceCol=0, formula="SUM($group.amount)"),
|
|
]),
|
|
Table(4, "People_summary_address", 0, 2, columns=[
|
|
Column(13, "address", "Ref:Address", False, "", summarySourceCol=7),
|
|
Column(14, "group", "RefList:People", True, summarySourceCol=0,
|
|
formula="table.getSummarySourceGroup(rec)"),
|
|
Column(15, "count", "Int", True, summarySourceCol=0, formula="len($group)"),
|
|
]),
|
|
])
|
|
self.assertTableData('People_summary_address', data=[
|
|
["id", "address", "count", "group"],
|
|
[1, 22, 1, [1]],
|
|
[2, 25, 1, [2]],
|
|
[3, 27, 1, [3]],
|
|
])
|
|
|
|
# Now remove the referenced table.
|
|
self.apply_user_action(["RemoveTable", "Address"])
|
|
# In both the People and summary tables, the 'address' reference column
|
|
# is converted to Int, because it didn't have a visible/display column.
|
|
self.assertTables([
|
|
Table(2, "People", primaryViewId=2, summarySourceTable=0, columns=[
|
|
Column(5, "manualSort", "ManualSortPos", False, "", 0),
|
|
Column(6, "name", "Text", False, "", 0),
|
|
Column(7, "address", "Int", False, "", 0),
|
|
Column(8, "city", "Any", True, "$address.city", 0),
|
|
]),
|
|
Table(4, "People_summary_address", 0, 2, columns=[
|
|
Column(13, "address", "Int", False, "", summarySourceCol=7),
|
|
Column(14, "group", "RefList:People", True, summarySourceCol=0,
|
|
formula="table.getSummarySourceGroup(rec)"),
|
|
Column(15, "count", "Int", True, summarySourceCol=0, formula="len($group)"),
|
|
]),
|
|
])
|
|
self.assertTableData('People', cols="subset", data=self.people_table_data)
|
|
self.assertTableData('People_summary_address', data=[
|
|
["id", "address", "count", "group"],
|
|
[1, 22, 1, [1]],
|
|
[2, 25, 1, [2]],
|
|
[3, 27, 1, [3]],
|
|
])
|
|
|
|
@test_engine.test_undo
|
|
def test_remove_table_referenced_by_summary_groupby_col_with_visible_col(self):
|
|
# Similar to the test above, but now the reference column has a visible column.
|
|
self.init_sample_data()
|
|
self.modify_column("People", "address", visibleCol=2)
|
|
self.apply_user_action(["SetDisplayFormula", "People", 0, 7, "$address.city"])
|
|
self.apply_user_action(["CreateViewSection", 2, 0, 'record', [7], None])
|
|
|
|
self.assertTables([
|
|
Table(1, "Address", primaryViewId=1, summarySourceTable=0, columns=[
|
|
Column(1, "manualSort", "ManualSortPos", False, "", 0),
|
|
Column(2, "city", "Text", False, "", 0),
|
|
Column(3, "state", "Text", False, "", 0),
|
|
Column(4, "amount", "Numeric", False, "", 0),
|
|
]),
|
|
Table(2, "People", primaryViewId=2, summarySourceTable=0, columns=[
|
|
Column(5, "manualSort", "ManualSortPos", False, "", 0),
|
|
Column(6, "name", "Text", False, "", 0),
|
|
Column(7, "address", "Ref:Address", False, "", 0),
|
|
Column(8, "city", "Any", True, "$address.city", 0),
|
|
Column(13, "gristHelper_Display", "Any", True, "$address.city", 0),
|
|
]),
|
|
Table(3, "Address_summary_state", 0, 1, columns=[
|
|
Column(9, "state", "Text", False, "", summarySourceCol=3),
|
|
Column(10, "group", "RefList:Address", True, summarySourceCol=0,
|
|
formula="table.getSummarySourceGroup(rec)"),
|
|
Column(11, "count", "Int", True, summarySourceCol=0, formula="len($group)"),
|
|
Column(12, "amount", "Numeric", True, summarySourceCol=0, formula="SUM($group.amount)"),
|
|
]),
|
|
Table(4, "People_summary_address", 0, 2, columns=[
|
|
Column(14, "address", "Ref:Address", False, "", summarySourceCol=7),
|
|
Column(15, "group", "RefList:People", True, summarySourceCol=0,
|
|
formula="table.getSummarySourceGroup(rec)"),
|
|
Column(16, "count", "Int", True, summarySourceCol=0, formula="len($group)"),
|
|
Column(17, "gristHelper_Display", "Any", True, "$address.city", 0),
|
|
]),
|
|
])
|
|
self.assertTableData('People_summary_address', data=[
|
|
["id", "address", "count", "group", "gristHelper_Display"],
|
|
[1, 22, 1, [1], "Albany"],
|
|
[2, 25, 1, [2], "Bedford"],
|
|
[3, 27, 1, [3], "Buffalo"],
|
|
])
|
|
|
|
self.apply_user_action(["RemoveTable", "Address"])
|
|
self.assertTables([
|
|
Table(2, "People", primaryViewId=2, summarySourceTable=0, columns=[
|
|
Column(5, "manualSort", "ManualSortPos", False, "", 0),
|
|
Column(6, "name", "Text", False, "", 0),
|
|
# Reference column is converted to the visible column type, i.e. Text.
|
|
Column(7, "address", "Text", False, "", 0),
|
|
Column(8, "city", "Any", True, "$address.city", 0),
|
|
]),
|
|
Table(4, "People_summary_address", 0, 2, columns=[
|
|
# Reference column is converted to the visible column type, i.e. Text.
|
|
Column(14, "address", "Text", False, "", summarySourceCol=7),
|
|
Column(15, "group", "RefList:People", True, summarySourceCol=0,
|
|
formula="table.getSummarySourceGroup(rec)"),
|
|
Column(16, "count", "Int", True, summarySourceCol=0, formula="len($group)"),
|
|
]),
|
|
])
|
|
self.assertTableData('People', cols="subset", data=[
|
|
["id", "name", "address" ],
|
|
[ 1, "Alice", "Albany"],
|
|
[ 2, "Bob", "Bedford"],
|
|
[ 3, "Carol", "Buffalo"],
|
|
])
|
|
self.assertTableData('People_summary_address', data=[
|
|
["id", "address", "count", "group"],
|
|
[ 4, "Albany", 1, [1]],
|
|
[ 5, "Bedford", 1, [2]],
|
|
[ 6, "Buffalo", 1, [3]],
|
|
])
|