1
+ /*
2
+ * Copyright The OpenTelemetry Authors
3
+ * Copyright 2025 Google LLC
4
+ *
5
+ * This file has been modified by Google LLC
6
+ *
7
+ * Licensed under the Apache License, Version 2.0 (the "License");
8
+ * you may not use this file except in compliance with the License.
9
+ * You may obtain a copy of the License at
10
+ *
11
+ * https://www.apache.org/licenses/LICENSE-2.0
12
+ *
13
+ * Unless required by applicable law or agreed to in writing, software
14
+ * distributed under the License is distributed on an "AS IS" BASIS,
15
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ * See the License for the specific language governing permissions and
17
+ * limitations under the License.
18
+ */
19
+
20
+ import * as sinon from 'sinon' ;
21
+ import * as assert from 'assert' ;
22
+ import { FetchTransportEdge } from './fetch-transport.edge' ;
23
+ import { ExportResponseRetryable , ExportResponseFailure , ExportResponseSuccess } from '@opentelemetry/otlp-exporter-base' ;
24
+
25
+ const testTransportParameters = {
26
+ url : 'http://example.test' ,
27
+ headers : ( ) => ( {
28
+ foo : 'foo-value' ,
29
+ bar : 'bar-value' ,
30
+ 'Content-Type' : 'application/json' ,
31
+ } ) ,
32
+ } ;
33
+
34
+ const requestTimeout = 1000 ;
35
+ const testPayload = Uint8Array . from ( [ 1 , 2 , 3 ] ) ;
36
+
37
+ describe ( 'FetchTransportEdge' , ( ) => {
38
+ afterEach ( ( ) => {
39
+ sinon . restore ( ) ;
40
+ } ) ;
41
+
42
+ describe ( 'send' , ( ) => {
43
+ it ( 'returns success when request succeeds' , ( done ) => {
44
+ // arrange
45
+ const fetchStub = sinon
46
+ . stub ( globalThis , 'fetch' )
47
+ . resolves ( new Response ( 'test response' , { status : 200 } ) ) ;
48
+ const transport = new FetchTransportEdge ( testTransportParameters ) ;
49
+
50
+ //act
51
+ transport . send ( testPayload , requestTimeout ) . then ( response => {
52
+ // assert
53
+ try {
54
+ assert . strictEqual ( response . status , 'success' ) ;
55
+ // currently we don't do anything with the response yet, so it's dropped by the transport.
56
+ assert . strictEqual (
57
+ ( response as ExportResponseSuccess ) . data ,
58
+ undefined
59
+ ) ;
60
+ sinon . assert . calledOnceWithMatch (
61
+ fetchStub ,
62
+ testTransportParameters . url ,
63
+ {
64
+ method : 'POST' ,
65
+ headers : {
66
+ foo : 'foo-value' ,
67
+ bar : 'bar-value' ,
68
+ 'Content-Type' : 'application/json' ,
69
+ } ,
70
+ body : testPayload ,
71
+ }
72
+ ) ;
73
+ done ( ) ;
74
+ } catch ( e ) {
75
+ done ( e ) ;
76
+ }
77
+ } , done /* catch any rejections */ ) ;
78
+ } ) ;
79
+
80
+ it ( 'returns failure when request fails' , ( done ) => {
81
+ // arrange
82
+ sinon
83
+ . stub ( globalThis , 'fetch' )
84
+ . resolves ( new Response ( '' , { status : 404 } ) ) ;
85
+ const transport = new FetchTransportEdge ( testTransportParameters ) ;
86
+
87
+ //act
88
+ transport . send ( testPayload , requestTimeout ) . then ( response => {
89
+ // assert
90
+ try {
91
+ assert . strictEqual ( response . status , 'failure' ) ;
92
+ done ( ) ;
93
+ } catch ( e ) {
94
+ done ( e ) ;
95
+ }
96
+ } , done /* catch any rejections */ ) ;
97
+ } ) ;
98
+
99
+ it ( 'returns retryable when request is retryable' , ( done ) => {
100
+ // arrange
101
+ sinon
102
+ . stub ( globalThis , 'fetch' )
103
+ . resolves (
104
+ new Response ( '' , { status : 503 , headers : { 'Retry-After' : '5' } } )
105
+ ) ;
106
+ const transport = new FetchTransportEdge ( testTransportParameters ) ;
107
+
108
+ //act
109
+ transport . send ( testPayload , requestTimeout ) . then ( response => {
110
+ // assert
111
+ try {
112
+ assert . strictEqual ( response . status , 'retryable' ) ;
113
+ assert . strictEqual (
114
+ ( response as ExportResponseRetryable ) . retryInMillis ,
115
+ 5000
116
+ ) ;
117
+ done ( ) ;
118
+ } catch ( e ) {
119
+ done ( e ) ;
120
+ }
121
+ } , done /* catch any rejections */ ) ;
122
+ } ) ;
123
+
124
+ it ( 'returns failure when request times out' , ( done ) => {
125
+ // arrange
126
+ const abortError = new Error ( 'aborted request' ) ;
127
+ abortError . name = 'AbortError' ;
128
+ sinon . stub ( globalThis , 'fetch' ) . rejects ( abortError ) ;
129
+ const clock = sinon . useFakeTimers ( ) ;
130
+ const transport = new FetchTransportEdge ( testTransportParameters ) ;
131
+
132
+ //act
133
+ transport . send ( testPayload , requestTimeout ) . then ( response => {
134
+ // assert
135
+ try {
136
+ assert . strictEqual ( response . status , 'failure' ) ;
137
+ assert . strictEqual (
138
+ ( response as ExportResponseFailure ) . error . message ,
139
+ 'aborted request'
140
+ ) ;
141
+ done ( ) ;
142
+ } catch ( e ) {
143
+ done ( e ) ;
144
+ }
145
+ } , done /* catch any rejections */ ) ;
146
+ clock . tick ( requestTimeout + 100 ) ;
147
+ } ) ;
148
+
149
+ it ( 'returns failure when no server exists' , ( done ) => {
150
+ // arrange
151
+ sinon . stub ( globalThis , 'fetch' ) . throws ( new Error ( 'fetch failed' ) ) ;
152
+ const clock = sinon . useFakeTimers ( ) ;
153
+ const transport = new FetchTransportEdge ( testTransportParameters ) ;
154
+
155
+ //act
156
+ transport . send ( testPayload , requestTimeout ) . then ( response => {
157
+ // assert
158
+ try {
159
+ assert . strictEqual ( response . status , 'failure' ) ;
160
+ assert . strictEqual (
161
+ ( response as ExportResponseFailure ) . error . message ,
162
+ 'fetch failed'
163
+ ) ;
164
+ done ( ) ;
165
+ } catch ( e ) {
166
+ done ( e ) ;
167
+ }
168
+ } , done /* catch any rejections */ ) ;
169
+ clock . tick ( requestTimeout + 100 ) ;
170
+ } ) ;
171
+ } ) ;
172
+ } ) ;
0 commit comments