Skip to content

Commit 465cea2

Browse files
committed
refactor(table): apply review findings from the quality pass
Four parallel reviews (reuse, simplification, efficiency, altitude) converged on the same set. Applied: Removed actorClientId entirely. It had no supplier anywhere in the repo, so every call reached signalTableRowsChangedByActor(id, undefined), which is byte-identical to the broadcast it replaced — three optional fields, three verbatim doc blocks and a pin test asserting the empty set, all inert. It belongs with the route migration that supplies an actor. The attribution pin is restored to its original form. The uniqueness probe was only half-narrowed: the patched column list was computed and then discarded, and the probe re-derived every unique column from the full schema. It now receives only the columns the patch touched, so a table with several unique columns runs one query instead of all of them. assertKnownColumnIds hand-rolled an id index and duplicated the sibling assert's message verbatim. It now reuses buildColumnNameById — which already keys by getColumnId, so the legacy pre-backfill column case is handled by the shared helper rather than by a special case here — and both asserts share one message builder. The job field list had become two copies, one drizzle-checked and one an unchecked sql<T> cast that could silently return undefined for a renamed field. The lateral now derives its jsonb pairs from JOB_PROJECTION, which satisfies Record<keyof LatestJobRow, Column>. That compile-time guarantee replaces the runtime drift test it makes redundant. Also: hoisted the id index out of the batch loop to match the names path, dropped a never-supplied parameter, narrowed an over-broad parameter type, removed a dead timer cleanup and its now-unused import, replaced dynamic re-imports with the static one already present, hoisted a repeated stub, and documented why filters need no keying counterpart and how laxness differs between the two wires. Verified to fail: removing a field from JOB_PROJECTION breaks the build in two places.
1 parent 1536278 commit 465cea2

9 files changed

Lines changed: 105 additions & 233 deletions

File tree

apps/sim/lib/table/__tests__/update-row.test.ts

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {
1414
upsertRow,
1515
} from '@/lib/table/rows/service'
1616
import type { TableDefinition } from '@/lib/table/types'
17-
import { getUniqueColumns } from '@/lib/table/validation'
17+
import { checkUniqueConstraintsDb, getUniqueColumns } from '@/lib/table/validation'
1818

1919
// Capacity is exercised in billing.test.ts; here it's a no-op so the timeout-scaling
2020
// suites can use large synthetic row counts without tripping the plan limit.
@@ -563,16 +563,16 @@ describe('updateRow — uniqueness probe scoping', () => {
563563
beforeEach(() => {
564564
vi.clearAllMocks()
565565
resetDbChainMock()
566+
// The common case: one unique column. The two tests that need a different
567+
// shape override this.
568+
vi.mocked(getUniqueColumns).mockReturnValue([{ name: 'name', type: 'string', unique: true }])
566569
dbChainMockFns.limit.mockResolvedValue([EXISTING_ROW])
567570
dbChainMockFns.returning.mockResolvedValue([
568571
{ id: EXISTING_ROW.id, updatedAt: PERSISTED_UPDATED_AT },
569572
])
570573
})
571574

572575
it('does not probe when the patch touches no unique column', async () => {
573-
const { checkUniqueConstraintsDb, getUniqueColumns } = await import('@/lib/table/validation')
574-
vi.mocked(getUniqueColumns).mockReturnValue([{ name: 'name', type: 'string', unique: true }])
575-
576576
await updateRow(
577577
{ tableId: 'tbl-1', rowId: 'row-1', data: { age: 31 }, workspaceId: 'ws-1' },
578578
TABLE,
@@ -583,9 +583,6 @@ describe('updateRow — uniqueness probe scoping', () => {
583583
})
584584

585585
it('still probes when the patch touches a unique column', async () => {
586-
const { checkUniqueConstraintsDb, getUniqueColumns } = await import('@/lib/table/validation')
587-
vi.mocked(getUniqueColumns).mockReturnValue([{ name: 'name', type: 'string', unique: true }])
588-
589586
await updateRow(
590587
{ tableId: 'tbl-1', rowId: 'row-1', data: { name: 'Grace' }, workspaceId: 'ws-1' },
591588
TABLE,
@@ -596,25 +593,40 @@ describe('updateRow — uniqueness probe scoping', () => {
596593
})
597594

598595
it('probes against the merged row, so the excluded row is still the one being edited', async () => {
599-
const { checkUniqueConstraintsDb, getUniqueColumns } = await import('@/lib/table/validation')
600-
vi.mocked(getUniqueColumns).mockReturnValue([{ name: 'name', type: 'string', unique: true }])
601-
602596
await updateRow(
603597
{ tableId: 'tbl-1', rowId: 'row-1', data: { name: 'Grace' }, workspaceId: 'ws-1' },
604598
TABLE,
605599
'req-1'
606600
)
607601

602+
// The merged row is what gets probed, the excluded row is the one being
603+
// edited, and the schema is narrowed to the unique columns this patch
604+
// touched — the probe issues one query per column it is handed.
608605
expect(checkUniqueConstraintsDb).toHaveBeenCalledWith(
609606
'tbl-1',
610607
{ name: 'Grace', age: 30 },
611-
TABLE.schema,
608+
{ ...TABLE.schema, columns: [{ name: 'name', type: 'string', unique: true }] },
612609
'row-1'
613610
)
614611
})
615612

613+
it('hands the probe only the unique columns the patch touched', async () => {
614+
vi.mocked(getUniqueColumns).mockReturnValue([
615+
{ name: 'name', type: 'string', unique: true },
616+
{ name: 'email', type: 'string', unique: true },
617+
])
618+
619+
await updateRow(
620+
{ tableId: 'tbl-1', rowId: 'row-1', data: { name: 'Grace' }, workspaceId: 'ws-1' },
621+
TABLE,
622+
'req-1'
623+
)
624+
625+
const schemaArg = vi.mocked(checkUniqueConstraintsDb).mock.calls[0][2]
626+
expect(schemaArg.columns).toEqual([{ name: 'name', type: 'string', unique: true }])
627+
})
628+
616629
it('surfaces a duplicate on a column the patch does write', async () => {
617-
const { checkUniqueConstraintsDb, getUniqueColumns } = await import('@/lib/table/validation')
618630
vi.mocked(getUniqueColumns).mockReturnValue([{ name: 'name', type: 'string', unique: true }])
619631
vi.mocked(checkUniqueConstraintsDb).mockResolvedValueOnce({
620632
valid: false,
@@ -631,7 +643,6 @@ describe('updateRow — uniqueness probe scoping', () => {
631643
})
632644

633645
it('does not probe on a table with no unique columns at all', async () => {
634-
const { checkUniqueConstraintsDb, getUniqueColumns } = await import('@/lib/table/validation')
635646
vi.mocked(getUniqueColumns).mockReturnValue([])
636647

637648
await updateRow(

apps/sim/lib/table/application/context.test.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* @vitest-environment node
33
*/
44

5-
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
5+
import { beforeEach, describe, expect, it, vi } from 'vitest'
66

77
const { getTableById, loadWorkspace } = vi.hoisted(() => ({
88
getTableById: vi.fn(),
@@ -60,10 +60,6 @@ describe('table application context', () => {
6060
)
6161
})
6262

63-
afterEach(() => {
64-
vi.useRealTimers()
65-
})
66-
6763
it('derives workspace scope from the canonical active table', async () => {
6864
await expect(
6965
resolveActiveTableContext({ tableId: 'table-1', assertedWorkspaceId: 'workspace-1' })

apps/sim/lib/table/application/rows.test.ts

Lines changed: 0 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ const {
1919
mockResolveContext,
2020
mockResolvePermission,
2121
mockSignalRowsChanged,
22-
mockSignalRowsChangedByActor,
2322
mockUpsertRow,
2423
mockWithLockedTable,
2524
mockInsertRow,
@@ -42,7 +41,6 @@ const {
4241
mockResolveContext: vi.fn(),
4342
mockResolvePermission: vi.fn(),
4443
mockSignalRowsChanged: vi.fn(),
45-
mockSignalRowsChangedByActor: vi.fn(),
4644
mockUpsertRow: vi.fn(),
4745
mockWithLockedTable: vi.fn(),
4846
mockInsertRow: vi.fn(),
@@ -139,12 +137,10 @@ vi.mock('@/lib/table/application/context', () => ({
139137

140138
vi.mock('@/lib/table/events', () => ({
141139
signalTableRowsChanged: mockSignalRowsChanged,
142-
signalTableRowsChangedByActor: mockSignalRowsChangedByActor,
143140
}))
144141

145142
import {
146143
createTableRows,
147-
deleteTableRow,
148144
deleteTableRows,
149145
listTableRows,
150146
ProjectedWireRowsValidationError,
@@ -1297,85 +1293,3 @@ describe('row data keying', () => {
12971293
).rejects.toThrow(/Row 2: Unknown columns: zzz, qqq/)
12981294
})
12991295
})
1300-
1301-
/**
1302-
* Naming the acting tab lets that tab skip refetching its own write. Only the
1303-
* single-row paths accept an actor — see `events.attribution.test.ts` for why,
1304-
* and for the pinned list of surfaces allowed to supply one.
1305-
*/
1306-
describe('row change attribution', () => {
1307-
beforeEach(() => {
1308-
vi.clearAllMocks()
1309-
mockResolvePermission.mockResolvedValue('write')
1310-
mockResolveContext.mockResolvedValue({
1311-
tableId: TABLE.id,
1312-
table: TABLE,
1313-
workspaceId: TABLE.workspaceId,
1314-
workspaceOrganizationId: 'organization-1',
1315-
allowPersonalApiKeys: true,
1316-
billedAccountUserId: 'billing-owner-1',
1317-
})
1318-
mockAssertRowCapacity.mockResolvedValue(10_000)
1319-
mockCreateSecretProvenance.mockReturnValue({ complete: true, columns: {} })
1320-
mockIsScopeCompatible.mockReturnValue(true)
1321-
})
1322-
1323-
it('names the acting tab on a single-row update', async () => {
1324-
await updateTableRow.execute({
1325-
principal: PRINCIPAL,
1326-
input: {
1327-
tableId: TABLE.id,
1328-
rowId: 'row-1',
1329-
data: { name: 'Ada' },
1330-
strictWrite: false,
1331-
dataKeying: 'names',
1332-
actorClientId: 'tab-42',
1333-
},
1334-
})
1335-
1336-
expect(mockSignalRowsChangedByActor).toHaveBeenCalledWith(TABLE.id, 'tab-42')
1337-
expect(mockSignalRowsChanged).not.toHaveBeenCalled()
1338-
})
1339-
1340-
it('broadcasts to everyone when the surface cannot name a tab', async () => {
1341-
await updateTableRow.execute({
1342-
principal: PRINCIPAL,
1343-
input: {
1344-
tableId: TABLE.id,
1345-
rowId: 'row-1',
1346-
data: { name: 'Ada' },
1347-
strictWrite: false,
1348-
dataKeying: 'names',
1349-
},
1350-
})
1351-
1352-
expect(mockSignalRowsChangedByActor).toHaveBeenCalledWith(TABLE.id, undefined)
1353-
})
1354-
1355-
it('names the acting tab on a single-row delete', async () => {
1356-
await deleteTableRow.execute({
1357-
principal: PRINCIPAL,
1358-
input: { tableId: TABLE.id, rowId: 'row-1', actorClientId: 'tab-42' },
1359-
})
1360-
1361-
expect(mockSignalRowsChangedByActor).toHaveBeenCalledWith(TABLE.id, 'tab-42')
1362-
})
1363-
1364-
it('broadcasts a batch insert to everyone even though a tab is named', async () => {
1365-
await createTableRows.execute({
1366-
principal: PRINCIPAL,
1367-
input: {
1368-
kind: 'batch',
1369-
tableId: TABLE.id,
1370-
rows: [{ name: 'Ada' }, { name: 'Grace' }],
1371-
strictWrite: false,
1372-
dataKeying: 'names',
1373-
},
1374-
})
1375-
1376-
// A batch write is not reconciled locally by the acting tab, so it must
1377-
// refetch like everyone else.
1378-
expect(mockSignalRowsChanged).toHaveBeenCalledWith(TABLE.id)
1379-
expect(mockSignalRowsChangedByActor).not.toHaveBeenCalled()
1380-
})
1381-
})

0 commit comments

Comments
 (0)