@@ -1680,6 +1680,7 @@ func TestProcessStreamCalculated(t *testing.T) {
16801680 Opts : []byte (`{"abi":[{"type":"int256","expression":"Sum(s1)","expressionStreamID":2}]}` ),
16811681 },
16821682 },
1683+ StreamAggregates : StreamAggregates {},
16831684 },
16841685 },
16851686 {
@@ -1694,6 +1695,7 @@ func TestProcessStreamCalculated(t *testing.T) {
16941695 Opts : []byte (`{"abi":[{"type":"int256","expression":"","expressionStreamID":2}]}` ),
16951696 },
16961697 },
1698+ StreamAggregates : StreamAggregates {},
16971699 },
16981700 },
16991701 {
@@ -1708,6 +1710,7 @@ func TestProcessStreamCalculated(t *testing.T) {
17081710 Opts : []byte (`{"abi":[{"type":"int256","expression":"s1","expressionStreamID":0}]}` ),
17091711 },
17101712 },
1713+ StreamAggregates : StreamAggregates {},
17111714 },
17121715 },
17131716 {
@@ -1837,3 +1840,127 @@ func TestProcessCalculatedStreamsDryRun(t *testing.T) {
18371840 })
18381841 }
18391842}
1843+
1844+ func TestProcessCalculatedStreams_AppendsExpressionStreamIDs (t * testing.T ) {
1845+ lggr , err := logger .New ()
1846+ require .NoError (t , err )
1847+
1848+ p := & Plugin {
1849+ Logger : lggr ,
1850+ }
1851+
1852+ assertHasCalculatedStream := func (t * testing.T , cd llotypes.ChannelDefinition , expressionStreamID llotypes.StreamID ) {
1853+ t .Helper ()
1854+ for _ , s := range cd .Streams {
1855+ if s .StreamID == expressionStreamID && s .Aggregator == llotypes .AggregatorCalculated {
1856+ return
1857+ }
1858+ }
1859+ t .Fatalf ("expected channel definition streams to include calculated streamID=%d aggregator=%v; got streams=%v" ,
1860+ expressionStreamID , llotypes .AggregatorCalculated , cd .Streams )
1861+ }
1862+
1863+ t .Run ("env population failure still appends expressionStreamID streams" , func (t * testing.T ) {
1864+ outcome := Outcome {
1865+ ObservationTimestampNanoseconds : 1750169759775700000 ,
1866+ ChannelDefinitions : llotypes.ChannelDefinitions {
1867+ 1 : {
1868+ ReportFormat : llotypes .ReportFormatEVMABIEncodeUnpackedExpr ,
1869+ Streams : []llotypes.Stream {{StreamID : 1 , Aggregator : llotypes .AggregatorMedian }},
1870+ Opts : []byte (`{"abi":[` +
1871+ `{"type":"int256","expression":"Sum(s1, 1)","expressionStreamID":2},` +
1872+ `{"type":"int256","expression":"Sum(s1, 2)","expressionStreamID":3}` +
1873+ `]}` ),
1874+ },
1875+ },
1876+ StreamAggregates : StreamAggregates {
1877+ // Intentionally omit stream 1 aggregate so env.SetStreamValue fails with "stream value is nil"
1878+ },
1879+ }
1880+
1881+ p .ProcessCalculatedStreams (& outcome )
1882+
1883+ cd := outcome .ChannelDefinitions [1 ]
1884+ assertHasCalculatedStream (t , cd , 2 )
1885+ assertHasCalculatedStream (t , cd , 3 )
1886+
1887+ // The implementation pre-creates map entries, but since env population failed, the values should be nil.
1888+ require .Contains (t , outcome .StreamAggregates , llotypes .StreamID (2 ), "map entry should be pre-created" )
1889+ require .Contains (t , outcome .StreamAggregates , llotypes .StreamID (3 ), "map entry should be pre-created" )
1890+
1891+ got2 := outcome .StreamAggregates [2 ][llotypes .AggregatorCalculated ]
1892+ got3 := outcome .StreamAggregates [3 ][llotypes .AggregatorCalculated ]
1893+ assert .Nil (t , got2 , "expected nil calculated aggregate for streamID=2 when env population fails" )
1894+ assert .Nil (t , got3 , "expected nil calculated aggregate for streamID=3 when env population fails" )
1895+ })
1896+
1897+ t .Run ("expression evaluation failure still appends expressionStreamID streams" , func (t * testing.T ) {
1898+ outcome := Outcome {
1899+ ObservationTimestampNanoseconds : 1750169759775700000 ,
1900+ ChannelDefinitions : llotypes.ChannelDefinitions {
1901+ 1 : {
1902+ ReportFormat : llotypes .ReportFormatEVMABIEncodeUnpackedExpr ,
1903+ Streams : []llotypes.Stream {{StreamID : 1 , Aggregator : llotypes .AggregatorMedian }},
1904+ Opts : []byte (`{"abi":[` +
1905+ `{"type":"int256","expression":"NonExistentFunc(s1)","expressionStreamID":2},` +
1906+ `{"type":"int256","expression":"NonExistentFunc(s1)","expressionStreamID":3}` +
1907+ `]}` ),
1908+ },
1909+ },
1910+ StreamAggregates : StreamAggregates {
1911+ 1 : {llotypes .AggregatorMedian : ToDecimal (decimal .NewFromInt (123 ))},
1912+ },
1913+ }
1914+
1915+ p .ProcessCalculatedStreams (& outcome )
1916+
1917+ cd := outcome .ChannelDefinitions [1 ]
1918+ assertHasCalculatedStream (t , cd , 2 )
1919+ assertHasCalculatedStream (t , cd , 3 )
1920+
1921+ // The implementation pre-creates map entries, but since eval failed, the values should be nil.
1922+ require .Contains (t , outcome .StreamAggregates , llotypes .StreamID (2 ), "map entry should be pre-created" )
1923+ require .Contains (t , outcome .StreamAggregates , llotypes .StreamID (3 ), "map entry should be pre-created" )
1924+
1925+ got2 := outcome .StreamAggregates [2 ][llotypes .AggregatorCalculated ]
1926+ got3 := outcome .StreamAggregates [3 ][llotypes .AggregatorCalculated ]
1927+ assert .Nil (t , got2 , "expected nil calculated aggregate for streamID=2 when expression evaluation fails" )
1928+ assert .Nil (t , got3 , "expected nil calculated aggregate for streamID=3 when expression evaluation fails" )
1929+ })
1930+
1931+ t .Run ("success appends expressionStreamID streams and sets calculated aggregates" , func (t * testing.T ) {
1932+ outcome := Outcome {
1933+ ObservationTimestampNanoseconds : 1750169759775700000 ,
1934+ ChannelDefinitions : llotypes.ChannelDefinitions {
1935+ 1 : {
1936+ ReportFormat : llotypes .ReportFormatEVMABIEncodeUnpackedExpr ,
1937+ Streams : []llotypes.Stream {{StreamID : 1 , Aggregator : llotypes .AggregatorMedian }},
1938+ Opts : []byte (`{"abi":[` +
1939+ `{"type":"int256","expression":"Sum(s1, 1)","expressionStreamID":2},` +
1940+ `{"type":"int256","expression":"Sum(s1, 2)","expressionStreamID":3}` +
1941+ `]}` ),
1942+ },
1943+ },
1944+ StreamAggregates : StreamAggregates {
1945+ 1 : {llotypes .AggregatorMedian : ToDecimal (decimal .NewFromInt (10 ))},
1946+ },
1947+ }
1948+
1949+ p .ProcessCalculatedStreams (& outcome )
1950+
1951+ cd := outcome .ChannelDefinitions [1 ]
1952+ assertHasCalculatedStream (t , cd , 2 )
1953+ assertHasCalculatedStream (t , cd , 3 )
1954+
1955+ require .Contains (t , outcome .StreamAggregates , llotypes .StreamID (2 ))
1956+ require .Contains (t , outcome .StreamAggregates , llotypes .StreamID (3 ))
1957+
1958+ got2 := outcome .StreamAggregates [2 ][llotypes .AggregatorCalculated ]
1959+ got3 := outcome .StreamAggregates [3 ][llotypes .AggregatorCalculated ]
1960+ require .NotNil (t , got2 )
1961+ require .NotNil (t , got3 )
1962+
1963+ assert .Equal (t , ToDecimal (decimal .NewFromInt (11 )), got2 )
1964+ assert .Equal (t , ToDecimal (decimal .NewFromInt (12 )), got3 )
1965+ })
1966+ }
0 commit comments