@@ -167,46 +167,46 @@ constexpr const char *kOneItemRejected =
167167TEST (ElasticsearchBulkResponseTests, ReportsSuccessWhenErrorsIsFalse)
168168{
169169 std::string reason;
170- EXPECT_TRUE (logs_exporter::detail::IsBulkResponseSuccessful (200 , kPrettySuccess , reason));
171- EXPECT_TRUE (logs_exporter::detail::IsBulkResponseSuccessful (200 , kCompactSuccess , reason));
170+ EXPECT_TRUE (logs_exporter::detail::IsBulkResponseSuccessful (200 , kPrettySuccess , 1 , reason));
171+ EXPECT_TRUE (logs_exporter::detail::IsBulkResponseSuccessful (200 , kCompactSuccess , 1 , reason));
172172}
173173
174174// A successful check must not leave a stale failure reason from a previous call behind.
175175TEST (ElasticsearchBulkResponseTests, ClearsFailureReasonOnSuccess)
176176{
177177 std::string reason = " stale" ;
178- EXPECT_TRUE (logs_exporter::detail::IsBulkResponseSuccessful (200 , kCompactSuccess , reason));
178+ EXPECT_TRUE (logs_exporter::detail::IsBulkResponseSuccessful (200 , kCompactSuccess , 1 , reason));
179179 EXPECT_TRUE (reason.empty ());
180180}
181181
182182TEST (ElasticsearchBulkResponseTests, ReportsFailureWhenAnyItemWasRejected)
183183{
184184 std::string reason;
185- EXPECT_FALSE (logs_exporter::detail::IsBulkResponseSuccessful (200 , kOneItemRejected , reason));
185+ EXPECT_FALSE (logs_exporter::detail::IsBulkResponseSuccessful (200 , kOneItemRejected , 2 , reason));
186186 EXPECT_NE (reason.find (" mapper_parsing_exception" ), std::string::npos);
187187}
188188
189189TEST (ElasticsearchBulkResponseTests, ReportsFailureOnUnusableBody)
190190{
191191 std::string reason;
192- EXPECT_FALSE (logs_exporter::detail::IsBulkResponseSuccessful (200 , " not json at all" , reason));
192+ EXPECT_FALSE (logs_exporter::detail::IsBulkResponseSuccessful (200 , " not json at all" , 1 , reason));
193193 EXPECT_FALSE (reason.empty ());
194194
195- EXPECT_FALSE (logs_exporter::detail::IsBulkResponseSuccessful (200 , " " , reason));
195+ EXPECT_FALSE (logs_exporter::detail::IsBulkResponseSuccessful (200 , " " , 1 , reason));
196196 EXPECT_FALSE (reason.empty ());
197197
198198 // An array rather than the expected object.
199- EXPECT_FALSE (logs_exporter::detail::IsBulkResponseSuccessful (200 , " [1,2,3]" , reason));
199+ EXPECT_FALSE (logs_exporter::detail::IsBulkResponseSuccessful (200 , " [1,2,3]" , 1 , reason));
200200 EXPECT_FALSE (reason.empty ());
201201}
202202
203203TEST (ElasticsearchBulkResponseTests, ReportsFailureWhenErrorsFieldIsMissingOrNotBoolean)
204204{
205205 std::string reason;
206206 EXPECT_FALSE (
207- logs_exporter::detail::IsBulkResponseSuccessful (200 , R"( {"took":30,"items":[]})" , reason));
207+ logs_exporter::detail::IsBulkResponseSuccessful (200 , R"( {"took":30,"items":[]})" , 1 , reason));
208208 EXPECT_FALSE (logs_exporter::detail::IsBulkResponseSuccessful (
209- 200 , R"( {"errors":"false","items":[]})" , reason));
209+ 200 , R"( {"errors":"false","items":[]})" , 0 , reason));
210210}
211211
212212// A non-2xx status is a failure even when the body reports errors:false. This is the invariant
@@ -215,22 +215,22 @@ TEST(ElasticsearchBulkResponseTests, ReportsFailureOnNon2xxStatus)
215215{
216216 std::string reason;
217217 const char *ok_body = R"( {"errors":false,"items":[]})" ;
218- EXPECT_FALSE (logs_exporter::detail::IsBulkResponseSuccessful (500 , ok_body, reason));
219- EXPECT_FALSE (logs_exporter::detail::IsBulkResponseSuccessful (429 , ok_body, reason));
220- EXPECT_FALSE (logs_exporter::detail::IsBulkResponseSuccessful (400 , ok_body, reason));
221- EXPECT_TRUE (logs_exporter::detail::IsBulkResponseSuccessful (200 , ok_body, reason));
218+ EXPECT_FALSE (logs_exporter::detail::IsBulkResponseSuccessful (500 , ok_body, 0 , reason));
219+ EXPECT_FALSE (logs_exporter::detail::IsBulkResponseSuccessful (429 , ok_body, 0 , reason));
220+ EXPECT_FALSE (logs_exporter::detail::IsBulkResponseSuccessful (400 , ok_body, 0 , reason));
221+ EXPECT_TRUE (logs_exporter::detail::IsBulkResponseSuccessful (200 , ok_body, 0 , reason));
222222}
223223
224224// errors:true with no extractable item error still fails, with the generic reason.
225225TEST (ElasticsearchBulkResponseTests, ReportsFailureWhenErrorsTrueWithoutItemError)
226226{
227227 std::string reason;
228228 EXPECT_FALSE (logs_exporter::detail::IsBulkResponseSuccessful (200 , R"( {"errors":true,"items":[]})" ,
229- reason));
229+ 0 , reason));
230230 EXPECT_FALSE (logs_exporter::detail::IsBulkResponseSuccessful (
231- 200 , R"( {"errors":true,"items":[null]})" , reason));
231+ 200 , R"( {"errors":true,"items":[null]})" , 1 , reason));
232232 EXPECT_FALSE (logs_exporter::detail::IsBulkResponseSuccessful (
233- 200 , R"( {"errors":true,"items":[{"index":42}]})" , reason));
233+ 200 , R"( {"errors":true,"items":[{"index":42}]})" , 1 , reason));
234234}
235235
236236// The 2xx range is the success band; the codes just outside it are failures. This pins the boundary
@@ -239,20 +239,32 @@ TEST(ElasticsearchBulkResponseTests, TreatsThe2xxRangeAsTheSuccessBand)
239239{
240240 std::string reason;
241241 const char *ok_body = R"( {"errors":false,"items":[]})" ;
242- EXPECT_FALSE (logs_exporter::detail::IsBulkResponseSuccessful (199 , ok_body, reason));
243- EXPECT_TRUE (logs_exporter::detail::IsBulkResponseSuccessful (200 , ok_body, reason));
244- EXPECT_TRUE (logs_exporter::detail::IsBulkResponseSuccessful (299 , ok_body, reason));
245- EXPECT_FALSE (logs_exporter::detail::IsBulkResponseSuccessful (300 , ok_body, reason));
242+ EXPECT_FALSE (logs_exporter::detail::IsBulkResponseSuccessful (199 , ok_body, 0 , reason));
243+ EXPECT_TRUE (logs_exporter::detail::IsBulkResponseSuccessful (200 , ok_body, 0 , reason));
244+ EXPECT_TRUE (logs_exporter::detail::IsBulkResponseSuccessful (299 , ok_body, 0 , reason));
245+ EXPECT_FALSE (logs_exporter::detail::IsBulkResponseSuccessful (300 , ok_body, 0 , reason));
246246}
247247
248- // Batch success is decided by the top-level "errors" flag alone. "items" is only read to extract a
249- // diagnostic reason on failure, so a response without "items" (for example one filtered with
250- // filter_path) is still a success when "errors" is false. This locks out re-introducing an
251- // items-count success check.
252- TEST (ElasticsearchBulkResponseTests, DoesNotRequireItemsForSuccess)
248+ // The exporter posts an unfiltered /_bulk, so Elasticsearch answers every submitted operation in
249+ // "items". A body that does not is not an answer to that request, and treating it as success would
250+ // hand the caller a write acknowledgement nobody made.
251+ TEST (ElasticsearchBulkResponseTests, RequiresAnAcknowledgementForEverySubmittedOperation)
253252{
254253 std::string reason;
255- EXPECT_TRUE (logs_exporter::detail::IsBulkResponseSuccessful (200 , R"( {"errors":false})" , reason));
254+ constexpr const char *kNoItems = R"( {"errors":false})" ;
255+ constexpr const char *kNullItems = R"( {"errors":false,"items":null})" ;
256+ constexpr const char *kOneItemBody =
257+ R"( {"errors":false,"items":[{"index":{"_shards":{"failed":0}}}]})" ;
258+ constexpr const char *kTwoItemBody = R"( {"errors":false,"items":[{"index":{}},{"index":{}}]})" ;
259+
260+ EXPECT_FALSE (logs_exporter::detail::IsBulkResponseSuccessful (200 , kNoItems , 1 , reason));
261+ EXPECT_FALSE (logs_exporter::detail::IsBulkResponseSuccessful (200 , kNullItems , 1 , reason));
262+ EXPECT_FALSE (logs_exporter::detail::IsBulkResponseSuccessful (200 , kOneItemBody , 2 , reason))
263+ << " too few acknowledgements" ;
264+ EXPECT_FALSE (logs_exporter::detail::IsBulkResponseSuccessful (200 , kTwoItemBody , 1 , reason))
265+ << " more acknowledgements than operations" ;
266+ EXPECT_TRUE (logs_exporter::detail::IsBulkResponseSuccessful (200 , kOneItemBody , 1 , reason));
267+ EXPECT_TRUE (logs_exporter::detail::IsBulkResponseSuccessful (200 , kTwoItemBody , 2 , reason));
256268}
257269// ---------------------------------------------------------------------------
258270// The response travelling from the HTTP callback to the verdict.
0 commit comments