@@ -38,7 +38,10 @@ const {
3838 InMemorySpanExporter,
3939} = require ( '@opentelemetry/sdk-trace-node' ) ;
4040// eslint-disable-next-line n/no-extraneous-require
41- const { SimpleSpanProcessor} = require ( '@opentelemetry/sdk-trace-base' ) ;
41+ const {
42+ ReadableSpan,
43+ SimpleSpanProcessor,
44+ } = require ( '@opentelemetry/sdk-trace-base' ) ;
4245import * as db from '../src/database' ;
4346import { Instance , MutationGroup , Spanner } from '../src' ;
4447import * as pfy from '@google-cloud/promisify' ;
@@ -1954,4 +1957,220 @@ describe('Database', () => {
19541957 fakeStream2 . push ( null ) ;
19551958 } ) ;
19561959 } ) ;
1960+
1961+ describe ( 'runPartitionedUpdate' , ( ) => {
1962+ const QUERY = {
1963+ sql : 'INSERT INTO `MyTable` (Key, Thing) VALUES(@key, @thing)' ,
1964+ params : {
1965+ key : 'k999' ,
1966+ thing : 'abc' ,
1967+ } ,
1968+ } ;
1969+
1970+ let fakePool : FakeSessionPool ;
1971+ let fakeSession : FakeSession ;
1972+ let fakePartitionedDml = new FakeTransaction (
1973+ { } as google . spanner . v1 . TransactionOptions . PartitionedDml
1974+ ) ;
1975+
1976+ let getSessionStub ;
1977+ let beginStub ;
1978+ let runUpdateStub ;
1979+
1980+ beforeEach ( ( ) => {
1981+ fakePool = database . pool_ ;
1982+ fakeSession = new FakeSession ( ) ;
1983+ fakePartitionedDml = new FakeTransaction (
1984+ { } as google . spanner . v1 . TransactionOptions . PartitionedDml
1985+ ) ;
1986+
1987+ getSessionStub = (
1988+ sandbox . stub ( fakePool , 'getSession' ) as sinon . SinonStub
1989+ ) . callsFake ( callback => {
1990+ callback ( null , fakeSession ) ;
1991+ } ) ;
1992+
1993+ sandbox . stub ( fakeSession , 'partitionedDml' ) . returns ( fakePartitionedDml ) ;
1994+
1995+ beginStub = (
1996+ sandbox . stub ( fakePartitionedDml , 'begin' ) as sinon . SinonStub
1997+ ) . callsFake ( callback => callback ( null ) ) ;
1998+
1999+ runUpdateStub = (
2000+ sandbox . stub ( fakePartitionedDml , 'runUpdate' ) as sinon . SinonStub
2001+ ) . callsFake ( ( _ , callback ) => callback ( null ) ) ;
2002+ } ) ;
2003+
2004+ interface traceExportResults {
2005+ spanNames : string [ ] ;
2006+ spans : ( typeof ReadableSpan ) [ ] ;
2007+ eventNames : string [ ] ;
2008+ }
2009+
2010+ async function getTraceExportResults ( ) : Promise < traceExportResults > {
2011+ await provider . forceFlush ( ) ;
2012+ await traceExporter . forceFlush ( ) ;
2013+ const spans = traceExporter . getFinishedSpans ( ) ;
2014+ withAllSpansHaveDBName ( spans ) ;
2015+
2016+ const actualSpanNames : string [ ] = [ ] ;
2017+ const actualEventNames : string [ ] = [ ] ;
2018+ spans . forEach ( span => {
2019+ actualSpanNames . push ( span . name ) ;
2020+ span . events . forEach ( event => {
2021+ actualEventNames . push ( event . name ) ;
2022+ } ) ;
2023+ } ) ;
2024+
2025+ return Promise . resolve ( {
2026+ spanNames : actualSpanNames ,
2027+ spans : spans ,
2028+ eventNames : actualEventNames ,
2029+ } ) ;
2030+ }
2031+
2032+ it ( 'with pool errors' , done => {
2033+ const fakeError = new Error ( 'err' ) ;
2034+ const fakeCallback = sandbox . spy ( ) ;
2035+
2036+ getSessionStub . callsFake ( callback => callback ( fakeError ) ) ;
2037+ database . runPartitionedUpdate ( QUERY , async ( err , rowCount ) => {
2038+ assert . strictEqual ( err , fakeError ) ;
2039+ assert . strictEqual ( rowCount , 0 ) ;
2040+
2041+ const exportResults = await getTraceExportResults ( ) ;
2042+ const actualSpanNames = exportResults . spanNames ;
2043+ const spans = exportResults . spans ;
2044+ const actualEventNames = exportResults . eventNames ;
2045+
2046+ const expectedSpanNames = [
2047+ 'CloudSpanner.Database.runPartitionedUpdate' ,
2048+ ] ;
2049+ assert . deepStrictEqual (
2050+ actualSpanNames ,
2051+ expectedSpanNames ,
2052+ `span names mismatch:\n\tGot: ${ actualSpanNames } \n\tWant: ${ expectedSpanNames } `
2053+ ) ;
2054+
2055+ // Ensure that the first span actually produced an error that was recorded.
2056+ const parentSpan = spans [ 0 ] ;
2057+ assert . deepStrictEqual (
2058+ SpanStatusCode . ERROR ,
2059+ parentSpan . status . code ,
2060+ 'Expected an ERROR span status'
2061+ ) ;
2062+ assert . deepStrictEqual (
2063+ fakeError . message ,
2064+ parentSpan . status . message . toString ( ) ,
2065+ 'Mismatched span status message'
2066+ ) ;
2067+
2068+ const expectedEventNames = [ ] ;
2069+ assert . deepStrictEqual (
2070+ actualEventNames ,
2071+ expectedEventNames ,
2072+ `Unexpected events:\n\tGot: ${ actualEventNames } \n\tWant: ${ expectedEventNames } `
2073+ ) ;
2074+
2075+ done ( ) ;
2076+ } ) ;
2077+ } ) ;
2078+
2079+ it ( 'with begin errors' , done => {
2080+ const fakeError = new Error ( 'err' ) ;
2081+
2082+ beginStub . callsFake ( callback => callback ( fakeError ) ) ;
2083+
2084+ const releaseStub = (
2085+ sandbox . stub ( fakePool , 'release' ) as sinon . SinonStub
2086+ ) . withArgs ( fakeSession ) ;
2087+
2088+ database . runPartitionedUpdate ( QUERY , async ( err , rowCount ) => {
2089+ assert . strictEqual ( err , fakeError ) ;
2090+ assert . strictEqual ( rowCount , 0 ) ;
2091+ assert . strictEqual ( releaseStub . callCount , 1 ) ;
2092+
2093+ const exportResults = await getTraceExportResults ( ) ;
2094+ const actualSpanNames = exportResults . spanNames ;
2095+ const spans = exportResults . spans ;
2096+ const actualEventNames = exportResults . eventNames ;
2097+
2098+ const expectedSpanNames = [
2099+ 'CloudSpanner.Database.runPartitionedUpdate' ,
2100+ ] ;
2101+ assert . deepStrictEqual (
2102+ actualSpanNames ,
2103+ expectedSpanNames ,
2104+ `span names mismatch:\n\tGot: ${ actualSpanNames } \n\tWant: ${ expectedSpanNames } `
2105+ ) ;
2106+
2107+ // Ensure that the first span actually produced an error that was recorded.
2108+ const parentSpan = spans [ 0 ] ;
2109+ assert . deepStrictEqual (
2110+ SpanStatusCode . ERROR ,
2111+ parentSpan . status . code ,
2112+ 'Expected an ERROR span status'
2113+ ) ;
2114+ assert . deepStrictEqual (
2115+ fakeError . message ,
2116+ parentSpan . status . message . toString ( ) ,
2117+ 'Mismatched span status message'
2118+ ) ;
2119+
2120+ const expectedEventNames = [ ] ;
2121+ assert . deepStrictEqual (
2122+ actualEventNames ,
2123+ expectedEventNames ,
2124+ `Unexpected events:\n\tGot: ${ actualEventNames } \n\tWant: ${ expectedEventNames } `
2125+ ) ;
2126+ done ( ) ;
2127+ } ) ;
2128+ } ) ;
2129+
2130+ it ( 'session released on transaction end' , done => {
2131+ const releaseStub = (
2132+ sandbox . stub ( fakePool , 'release' ) as sinon . SinonStub
2133+ ) . withArgs ( fakeSession ) ;
2134+
2135+ database . runPartitionedUpdate ( QUERY , async ( err , rowCount ) => {
2136+ const exportResults = await getTraceExportResults ( ) ;
2137+ const actualSpanNames = exportResults . spanNames ;
2138+ const spans = exportResults . spans ;
2139+ const actualEventNames = exportResults . eventNames ;
2140+
2141+ const expectedSpanNames = [
2142+ 'CloudSpanner.Database.runPartitionedUpdate' ,
2143+ ] ;
2144+ assert . deepStrictEqual (
2145+ actualSpanNames ,
2146+ expectedSpanNames ,
2147+ `span names mismatch:\n\tGot: ${ actualSpanNames } \n\tWant: ${ expectedSpanNames } `
2148+ ) ;
2149+
2150+ // Ensure that the first span actually produced an error that was recorded.
2151+ const parentSpan = spans [ 0 ] ;
2152+ assert . deepStrictEqual (
2153+ SpanStatusCode . UNSET ,
2154+ parentSpan . status . code ,
2155+ 'Unexpected span status'
2156+ ) ;
2157+ assert . deepStrictEqual (
2158+ undefined ,
2159+ parentSpan . status . message ,
2160+ 'Mismatched span status message'
2161+ ) ;
2162+
2163+ const expectedEventNames = [ ] ;
2164+ assert . deepStrictEqual (
2165+ actualEventNames ,
2166+ expectedEventNames ,
2167+ `Unexpected events:\n\tGot: ${ actualEventNames } \n\tWant: ${ expectedEventNames } `
2168+ ) ;
2169+ done ( ) ;
2170+ } ) ;
2171+
2172+ fakePartitionedDml . emit ( 'end' ) ;
2173+ assert . strictEqual ( releaseStub . callCount , 1 ) ;
2174+ } ) ;
2175+ } ) ;
19572176} ) ;
0 commit comments