@@ -313,6 +313,252 @@ exit 0
313
313
EXPECT_EQ (last_hash, 0 );
314
314
}
315
315
316
+ TEST_F (InventoryAPITests, PushInventoryDataNumericTypesTest) {
317
+ string script = R"( #!/bin/sh
318
+ echo "storage_used=63"
319
+ echo "cpu_count=4"
320
+ echo "memory_total=8192"
321
+ echo "temperature=23.5"
322
+ echo "device_name=raspberry-pi"
323
+ echo "enabled=true"
324
+ echo "negative_value=-100"
325
+ exit 0
326
+ )" ;
327
+ auto ret = PrepareTestScript (" mender-inventory-script1" , script);
328
+ ASSERT_TRUE (ret);
329
+
330
+ mtesting::TestEventLoop loop;
331
+
332
+ http::ServerConfig server_config;
333
+ http::Server server (server_config, loop);
334
+
335
+ http::ClientConfig client_config;
336
+ NoAuthHTTPClient client {client_config, loop};
337
+
338
+ // Expected JSON with numeric values NOT quoted
339
+ const string expected_request_data =
340
+ R"( [{"name":"cpu_count","value":4},{"name":"device_name","value":"raspberry-pi"},{"name":"enabled","value":"true"},{"name":"memory_total","value":8192},{"name":"mender_client_version","value":")"
341
+ + conf::kMenderVersion
342
+ + R"( "},{"name":"negative_value","value":-100},{"name":"storage_used","value":63},{"name":"temperature","value":23.5}])" ;
343
+
344
+ vector<uint8_t > received_body;
345
+ server.AsyncServeUrl (
346
+ TEST_SERVER,
347
+ [&received_body, &expected_request_data](http::ExpectedIncomingRequestPtr exp_req) {
348
+ ASSERT_TRUE (exp_req) << exp_req.error ().String ();
349
+ auto req = exp_req.value ();
350
+
351
+ auto content_length = req->GetHeader (" Content-Length" );
352
+ ASSERT_TRUE (content_length);
353
+ EXPECT_EQ (content_length.value (), to_string (expected_request_data.size ()));
354
+ auto ex_len = common::StringToLongLong (content_length.value ());
355
+ ASSERT_TRUE (ex_len);
356
+
357
+ auto body_writer = make_shared<io::ByteWriter>(received_body);
358
+ received_body.resize (ex_len.value ());
359
+ req->SetBodyWriter (body_writer);
360
+ },
361
+ [&received_body, &expected_request_data](http::ExpectedIncomingRequestPtr exp_req) {
362
+ ASSERT_TRUE (exp_req) << exp_req.error ().String ();
363
+
364
+ auto req = exp_req.value ();
365
+ EXPECT_EQ (req->GetPath (), " /api/devices/v1/inventory/device/attributes" );
366
+ EXPECT_EQ (req->GetMethod (), http::Method::PUT);
367
+ EXPECT_EQ (common::StringFromByteVector (received_body), expected_request_data);
368
+
369
+ auto result = req->MakeResponse ();
370
+ ASSERT_TRUE (result);
371
+ auto resp = result.value ();
372
+
373
+ resp->SetHeader (" Content-Length" , " 0" );
374
+ resp->SetStatusCodeAndMessage (200 , " Success" );
375
+ resp->AsyncReply ([](error::Error err) { ASSERT_EQ (error::NoError, err); });
376
+ });
377
+
378
+ bool handler_called = false ;
379
+ size_t last_hash = 0 ;
380
+ auto err = inv::PushInventoryData (
381
+ test_scripts_dir.Path (),
382
+ loop,
383
+ client,
384
+ last_hash,
385
+ [&handler_called, &loop](error::Error err) {
386
+ handler_called = true ;
387
+ ASSERT_EQ (err, error::NoError);
388
+ loop.Stop ();
389
+ });
390
+ EXPECT_EQ (err, error::NoError);
391
+
392
+ loop.Run ();
393
+ EXPECT_TRUE (handler_called);
394
+ EXPECT_EQ (last_hash, std::hash<string> {}(expected_request_data));
395
+ }
396
+
397
+ TEST_F (InventoryAPITests, PushInventoryDataMixedArrayTypesTest) {
398
+ string script = R"( #!/bin/sh
399
+ echo "numbers=42"
400
+ echo "numbers=3.14"
401
+ echo "numbers=text"
402
+ echo "numbers=-7"
403
+ exit 0
404
+ )" ;
405
+ auto ret = PrepareTestScript (" mender-inventory-script1" , script);
406
+ ASSERT_TRUE (ret);
407
+
408
+ mtesting::TestEventLoop loop;
409
+
410
+ http::ServerConfig server_config;
411
+ http::Server server (server_config, loop);
412
+
413
+ http::ClientConfig client_config;
414
+ NoAuthHTTPClient client {client_config, loop};
415
+
416
+ // Expected JSON with mixed array: [42, 3.14, "text", -7]
417
+ const string expected_request_data = R"( [{"name":"mender_client_version","value":")"
418
+ + conf::kMenderVersion
419
+ + R"( "},{"name":"numbers","value":[42,3.14,"text",-7]}])" ;
420
+
421
+ vector<uint8_t > received_body;
422
+ server.AsyncServeUrl (
423
+ TEST_SERVER,
424
+ [&received_body, &expected_request_data](http::ExpectedIncomingRequestPtr exp_req) {
425
+ ASSERT_TRUE (exp_req) << exp_req.error ().String ();
426
+ auto req = exp_req.value ();
427
+
428
+ auto content_length = req->GetHeader (" Content-Length" );
429
+ ASSERT_TRUE (content_length);
430
+ EXPECT_EQ (content_length.value (), to_string (expected_request_data.size ()));
431
+ auto ex_len = common::StringToLongLong (content_length.value ());
432
+ ASSERT_TRUE (ex_len);
433
+
434
+ auto body_writer = make_shared<io::ByteWriter>(received_body);
435
+ received_body.resize (ex_len.value ());
436
+ req->SetBodyWriter (body_writer);
437
+ },
438
+ [&received_body, &expected_request_data](http::ExpectedIncomingRequestPtr exp_req) {
439
+ ASSERT_TRUE (exp_req) << exp_req.error ().String ();
440
+
441
+ auto req = exp_req.value ();
442
+ EXPECT_EQ (req->GetPath (), " /api/devices/v1/inventory/device/attributes" );
443
+ EXPECT_EQ (req->GetMethod (), http::Method::PUT);
444
+ EXPECT_EQ (common::StringFromByteVector (received_body), expected_request_data);
445
+
446
+ auto result = req->MakeResponse ();
447
+ ASSERT_TRUE (result);
448
+ auto resp = result.value ();
449
+
450
+ resp->SetHeader (" Content-Length" , " 0" );
451
+ resp->SetStatusCodeAndMessage (200 , " Success" );
452
+ resp->AsyncReply ([](error::Error err) { ASSERT_EQ (error::NoError, err); });
453
+ });
454
+
455
+ bool handler_called = false ;
456
+ size_t last_hash = 0 ;
457
+ auto err = inv::PushInventoryData (
458
+ test_scripts_dir.Path (),
459
+ loop,
460
+ client,
461
+ last_hash,
462
+ [&handler_called, &loop](error::Error err) {
463
+ handler_called = true ;
464
+ ASSERT_EQ (err, error::NoError);
465
+ loop.Stop ();
466
+ });
467
+ EXPECT_EQ (err, error::NoError);
468
+
469
+ loop.Run ();
470
+ EXPECT_TRUE (handler_called);
471
+ EXPECT_EQ (last_hash, std::hash<string> {}(expected_request_data));
472
+ }
473
+
474
+ TEST_F (InventoryAPITests, PushInventoryDataEdgeCasesTest) {
475
+ string script = R"( #!/bin/sh
476
+ echo "partial_numeric=123abc"
477
+ echo "leading_space= 42"
478
+ echo "trailing_space=42 "
479
+ echo "infinity_val=inf"
480
+ echo "nan_val=nan"
481
+ echo "plus_sign=+42"
482
+ echo "scientific_upper=1.23E-4"
483
+ echo "hex_like=0x123"
484
+ echo "empty_val="
485
+ echo "just_minus=-"
486
+ echo "just_plus=+"
487
+ echo "just_dot=."
488
+ exit 0
489
+ )" ;
490
+ auto ret = PrepareTestScript (" mender-inventory-script1" , script);
491
+ ASSERT_TRUE (ret);
492
+
493
+ mtesting::TestEventLoop loop;
494
+
495
+ http::ServerConfig server_config;
496
+ http::Server server (server_config, loop);
497
+
498
+ http::ClientConfig client_config;
499
+ NoAuthHTTPClient client {client_config, loop};
500
+
501
+ // Expected JSON - edge cases properly handled: numeric when valid, string when not
502
+ // Note: hex is parsed as numeric by strtod, leading space is trimmed, scientific notation
503
+ // preserved
504
+ const string expected_request_data =
505
+ R"( [{"name":"empty_val","value":""},{"name":"hex_like","value":0x123},{"name":"infinity_val","value":"inf"},{"name":"just_dot","value":"."},{"name":"just_minus","value":"-"},{"name":"just_plus","value":"+"},{"name":"leading_space","value": 42},{"name":"mender_client_version","value":")"
506
+ + conf::kMenderVersion
507
+ + R"( "},{"name":"nan_val","value":"nan"},{"name":"partial_numeric","value":"123abc"},{"name":"plus_sign","value":+42},{"name":"scientific_upper","value":1.23E-4},{"name":"trailing_space","value":"42 "}])" ;
508
+
509
+ vector<uint8_t > received_body;
510
+ server.AsyncServeUrl (
511
+ TEST_SERVER,
512
+ [&received_body, &expected_request_data](http::ExpectedIncomingRequestPtr exp_req) {
513
+ ASSERT_TRUE (exp_req) << exp_req.error ().String ();
514
+ auto req = exp_req.value ();
515
+
516
+ auto content_length = req->GetHeader (" Content-Length" );
517
+ ASSERT_TRUE (content_length);
518
+ EXPECT_EQ (content_length.value (), to_string (expected_request_data.size ()));
519
+ auto ex_len = common::StringToLongLong (content_length.value ());
520
+ ASSERT_TRUE (ex_len);
521
+
522
+ auto body_writer = make_shared<io::ByteWriter>(received_body);
523
+ received_body.resize (ex_len.value ());
524
+ req->SetBodyWriter (body_writer);
525
+ },
526
+ [&received_body, &expected_request_data](http::ExpectedIncomingRequestPtr exp_req) {
527
+ ASSERT_TRUE (exp_req) << exp_req.error ().String ();
528
+
529
+ auto req = exp_req.value ();
530
+ EXPECT_EQ (req->GetPath (), " /api/devices/v1/inventory/device/attributes" );
531
+ EXPECT_EQ (req->GetMethod (), http::Method::PUT);
532
+ EXPECT_EQ (common::StringFromByteVector (received_body), expected_request_data);
533
+
534
+ auto result = req->MakeResponse ();
535
+ ASSERT_TRUE (result);
536
+ auto resp = result.value ();
537
+
538
+ resp->SetHeader (" Content-Length" , " 0" );
539
+ resp->SetStatusCodeAndMessage (200 , " Success" );
540
+ resp->AsyncReply ([](error::Error err) { ASSERT_EQ (error::NoError, err); });
541
+ });
542
+
543
+ bool handler_called = false ;
544
+ size_t last_hash = 0 ;
545
+ auto err = inv::PushInventoryData (
546
+ test_scripts_dir.Path (),
547
+ loop,
548
+ client,
549
+ last_hash,
550
+ [&handler_called, &loop](error::Error err) {
551
+ handler_called = true ;
552
+ ASSERT_EQ (err, error::NoError);
553
+ loop.Stop ();
554
+ });
555
+ EXPECT_EQ (err, error::NoError);
556
+
557
+ loop.Run ();
558
+ EXPECT_TRUE (handler_called);
559
+ EXPECT_EQ (last_hash, std::hash<string> {}(expected_request_data));
560
+ }
561
+
316
562
TEST_F (InventoryAPITests, PushInventoryDataNoopTest) {
317
563
string script = R"( #!/bin/sh
318
564
echo "key1=value1"
0 commit comments