UNPKG

@electric-sql/d2mini

Version:

D2Mini is a minimal implementation of Differential Dataflow for performing in-memory incremental view maintenance.

215 lines (182 loc) 5.01 kB
import { describe, test, expect } from 'vitest' import { D2 } from '../../src/d2.js' import { MultiSet } from '../../src/multiset.js' import { count } from '../../src/operators/count.js' import { output } from '../../src/operators/output.js' import { KeyedMessageTracker, assertKeyedResults, assertOnlyKeysAffected, } from '../test-utils.js' describe('Operators', () => { describe('Count operation', () => { testCount() }) }) function testCount() { test('basic count operation', () => { const graph = new D2() const input = graph.newInput<[number, string]>() const tracker = new KeyedMessageTracker<number, number>() input.pipe( count(), output((message) => { tracker.addMessage(message) }), ) graph.finalize() input.sendData( new MultiSet([ [[1, 'a'], 2], [[2, 'b'], 1], [[2, 'c'], 1], [[2, 'd'], 1], [[3, 'x'], 1], [[3, 'y'], -1], ]), ) input.sendData(new MultiSet([[[3, 'z'], 1]])) graph.run() const result = tracker.getResult() // Assert only keys that have values are affected assertOnlyKeysAffected('basic count operation', result.messages, [1, 2, 3]) // Assert the final materialized results are correct assertKeyedResults( 'basic count operation', result, [ [1, 2], // 2 values for key 1 [2, 3], // 3 values for key 2 [3, 1], // 1 value for key 3 (1 + (-1) + 1 = 1) ], 6, // Expected message count ) }) test('count with all negative multiplicities', () => { const graph = new D2() const input = graph.newInput<[number, string]>() const tracker = new KeyedMessageTracker<number, number>() input.pipe( count(), output((message) => { tracker.addMessage(message) }), ) graph.finalize() input.sendData( new MultiSet([ [[1, 'a'], -1], [[1, 'b'], -2], ]), ) graph.run() const result = tracker.getResult() // Assert only key 1 is affected assertOnlyKeysAffected( 'count with all negative multiplicities', result.messages, [1], ) // Assert the final materialized results are correct assertKeyedResults( 'count with all negative multiplicities', result, [ [1, -3], // -1 + (-2) = -3 ], 2, // Expected message count ) }) test('count with multiple batches', () => { const graph = new D2() const input = graph.newInput<[string, string]>() const tracker = new KeyedMessageTracker<string, number>() input.pipe( count(), output((message) => { tracker.addMessage(message) }), ) graph.finalize() input.sendData( new MultiSet([ [['one', 'a'], 1], [['one', 'b'], 1], ]), ) graph.run() input.sendData( new MultiSet([ [['one', 'c'], 1], [['two', 'a'], 1], ]), ) graph.run() const result = tracker.getResult() // Assert only keys 'one' and 'two' are affected assertOnlyKeysAffected('count with multiple batches', result.messages, [ 'one', 'two', ]) // Assert the final materialized results are correct assertKeyedResults( 'count with multiple batches', result, [ ['one', 3], // 2 + 1 = 3 ['two', 1], // 1 ], 5, // Expected message count ) }) test('count incremental updates - only affected keys produce messages', () => { const graph = new D2() const input = graph.newInput<[string, string]>() const tracker = new KeyedMessageTracker<string, number>() input.pipe( count(), output((message) => { tracker.addMessage(message) }), ) graph.finalize() // Initial data: establish state for keys 'a', 'b', 'c' input.sendData( new MultiSet([ [['a', 'item1'], 1], [['a', 'item2'], 1], [['b', 'item1'], 1], [['b', 'item2'], 1], [['b', 'item3'], 1], [['c', 'item1'], 1], ]), ) graph.run() // Reset tracker to focus on incremental updates tracker.reset() // Incremental update: only affect keys 'a' and 'c' input.sendData( new MultiSet([ [['a', 'item3'], 1], // Add to 'a' (2 -> 3) [['c', 'item1'], -1], // Remove from 'c' (1 -> 0) ]), ) graph.run() const result = tracker.getResult() // Assert only keys 'a' and 'c' are affected (NOT 'b') assertOnlyKeysAffected('count incremental updates', result.messages, [ 'a', 'c', ]) // Assert the final materialized results are correct assertKeyedResults( 'count incremental updates', result, [ ['a', 3], // Count increased from 2 to 3 ['c', 0], // Count decreased from 1 to 0 ], 4, // Expected message count: remove old 'a', add new 'a', remove old 'c', add new 'c' ) }) }